Annotation of src/usr.bin/ssh/sftp-client.c, Revision 1.1
1.1 ! djm 1: /*
! 2: * Copyright (c) 2001 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: redesign to allow concurrent overlapped operations */
! 28: /* XXX: we use fatal too much, error may be more appropriate in places */
! 29: /* XXX: copy between two remote sites */
! 30:
! 31: #include "includes.h"
! 32: RCSID("$OpenBSD$");
! 33:
! 34: #include "ssh.h"
! 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 "pathnames.h"
! 42:
! 43: #include "sftp.h"
! 44: #include "sftp-common.h"
! 45: #include "sftp-client.h"
! 46:
! 47: /* How much data to read/write at at time during copies */
! 48: /* XXX: what should this be? */
! 49: #define COPY_SIZE 8192
! 50:
! 51: void
! 52: send_msg(int fd, Buffer *m)
! 53: {
! 54: int mlen = buffer_len(m);
! 55: int len;
! 56: Buffer oqueue;
! 57:
! 58: buffer_init(&oqueue);
! 59: buffer_put_int(&oqueue, mlen);
! 60: buffer_append(&oqueue, buffer_ptr(m), mlen);
! 61: buffer_consume(m, mlen);
! 62:
! 63: len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
! 64: if (len <= 0)
! 65: fatal("Couldn't send packet: %s", strerror(errno));
! 66:
! 67: buffer_free(&oqueue);
! 68: }
! 69:
! 70: void
! 71: get_msg(int fd, Buffer *m)
! 72: {
! 73: u_int len, msg_len;
! 74: unsigned char buf[4096];
! 75:
! 76: len = atomicio(read, fd, buf, 4);
! 77: if (len != 4)
! 78: fatal("Couldn't read packet: %s", strerror(errno));
! 79:
! 80: msg_len = GET_32BIT(buf);
! 81: if (msg_len > 256 * 1024)
! 82: fatal("Received message too long %d", msg_len);
! 83:
! 84: while (msg_len) {
! 85: len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
! 86: if (len <= 0)
! 87: fatal("Couldn't read packet: %s", strerror(errno));
! 88:
! 89: msg_len -= len;
! 90: buffer_append(m, buf, len);
! 91: }
! 92: }
! 93:
! 94: void
! 95: send_string_request(int fd, u_int id, u_int code, char *s,
! 96: u_int len)
! 97: {
! 98: Buffer msg;
! 99:
! 100: buffer_init(&msg);
! 101: buffer_put_char(&msg, code);
! 102: buffer_put_int(&msg, id);
! 103: buffer_put_string(&msg, s, len);
! 104: send_msg(fd, &msg);
! 105: debug3("Sent message fd %d T:%d I:%d", fd, code, id);
! 106: buffer_free(&msg);
! 107: }
! 108:
! 109: void
! 110: send_string_attrs_request(int fd, u_int id, u_int code, char *s,
! 111: u_int len, Attrib *a)
! 112: {
! 113: Buffer msg;
! 114:
! 115: buffer_init(&msg);
! 116: buffer_put_char(&msg, code);
! 117: buffer_put_int(&msg, id);
! 118: buffer_put_string(&msg, s, len);
! 119: encode_attrib(&msg, a);
! 120: send_msg(fd, &msg);
! 121: debug3("Sent message fd %d T:%d I:%d", fd, code, id);
! 122: buffer_free(&msg);
! 123: }
! 124:
! 125: u_int
! 126: get_status(int fd, int expected_id)
! 127: {
! 128: Buffer msg;
! 129: u_int type, id, status;
! 130:
! 131: buffer_init(&msg);
! 132: get_msg(fd, &msg);
! 133: type = buffer_get_char(&msg);
! 134: id = buffer_get_int(&msg);
! 135:
! 136: if (id != expected_id)
! 137: fatal("ID mismatch (%d != %d)", id, expected_id);
! 138: if (type != SSH2_FXP_STATUS)
! 139: fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d",
! 140: SSH2_FXP_STATUS, type);
! 141:
! 142: status = buffer_get_int(&msg);
! 143: buffer_free(&msg);
! 144:
! 145: debug3("SSH2_FXP_STATUS %d", status);
! 146:
! 147: return(status);
! 148: }
! 149:
! 150: char *
! 151: get_handle(int fd, u_int expected_id, u_int *len)
! 152: {
! 153: Buffer msg;
! 154: u_int type, id;
! 155: char *handle;
! 156:
! 157: buffer_init(&msg);
! 158: get_msg(fd, &msg);
! 159: type = buffer_get_char(&msg);
! 160: id = buffer_get_int(&msg);
! 161:
! 162: if (id != expected_id)
! 163: fatal("ID mismatch (%d != %d)", id, expected_id);
! 164: if (type == SSH2_FXP_STATUS) {
! 165: int status = buffer_get_int(&msg);
! 166:
! 167: error("Couldn't get handle: %s", fx2txt(status));
! 168: return(NULL);
! 169: } else if (type != SSH2_FXP_HANDLE)
! 170: fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d",
! 171: SSH2_FXP_HANDLE, type);
! 172:
! 173: handle = buffer_get_string(&msg, len);
! 174: buffer_free(&msg);
! 175:
! 176: return(handle);
! 177: }
! 178:
! 179: Attrib *
! 180: get_decode_stat(int fd, u_int expected_id)
! 181: {
! 182: Buffer msg;
! 183: u_int type, id;
! 184: Attrib *a;
! 185:
! 186: buffer_init(&msg);
! 187: get_msg(fd, &msg);
! 188:
! 189: type = buffer_get_char(&msg);
! 190: id = buffer_get_int(&msg);
! 191:
! 192: debug3("Received stat reply T:%d I:%d", type, id);
! 193: if (id != expected_id)
! 194: fatal("ID mismatch (%d != %d)", id, expected_id);
! 195: if (type == SSH2_FXP_STATUS) {
! 196: int status = buffer_get_int(&msg);
! 197:
! 198: error("Couldn't stat remote file: %s", fx2txt(status));
! 199: return(NULL);
! 200: } else if (type != SSH2_FXP_ATTRS) {
! 201: fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d",
! 202: SSH2_FXP_ATTRS, type);
! 203: }
! 204: a = decode_attrib(&msg);
! 205: buffer_free(&msg);
! 206:
! 207: return(a);
! 208: }
! 209:
! 210: int
! 211: do_init(int fd_in, int fd_out)
! 212: {
! 213: int type, version;
! 214: Buffer msg;
! 215:
! 216: buffer_init(&msg);
! 217: buffer_put_char(&msg, SSH2_FXP_INIT);
! 218: buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
! 219: send_msg(fd_out, &msg);
! 220:
! 221: buffer_clear(&msg);
! 222:
! 223: get_msg(fd_in, &msg);
! 224:
! 225: /* Expecting a VERSION reply */
! 226: if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
! 227: error("Invalid packet back from SSH2_FXP_INIT (type %d)",
! 228: type);
! 229: buffer_free(&msg);
! 230: return(-1);
! 231: }
! 232: version = buffer_get_int(&msg);
! 233:
! 234: debug2("Remote version: %d", version);
! 235:
! 236: /* Check for extensions */
! 237: while (buffer_len(&msg) > 0) {
! 238: char *name = buffer_get_string(&msg, NULL);
! 239: char *value = buffer_get_string(&msg, NULL);
! 240:
! 241: debug2("Init extension: \"%s\"", name);
! 242: xfree(name);
! 243: xfree(value);
! 244: }
! 245:
! 246: buffer_free(&msg);
! 247: return(0);
! 248: }
! 249:
! 250: int
! 251: do_close(int fd_in, int fd_out, char *handle, u_int handle_len)
! 252: {
! 253: u_int id, status;
! 254: Buffer msg;
! 255:
! 256: buffer_init(&msg);
! 257:
! 258: id = arc4random();
! 259: buffer_put_char(&msg, SSH2_FXP_CLOSE);
! 260: buffer_put_int(&msg, id);
! 261: buffer_put_string(&msg, handle, handle_len);
! 262: send_msg(fd_out, &msg);
! 263: debug3("Sent message SSH2_FXP_CLOSE I:%d", id);
! 264:
! 265: status = get_status(fd_in, id);
! 266: if (status != SSH2_FX_OK)
! 267: error("Couldn't close file: %s", fx2txt(status));
! 268:
! 269: buffer_free(&msg);
! 270:
! 271: return(status);
! 272: }
! 273:
! 274: int
! 275: do_ls(int fd_in, int fd_out, char *path)
! 276: {
! 277: Buffer msg;
! 278: u_int type, id, handle_len, i, expected_id;
! 279: char *handle;
! 280:
! 281: id = arc4random();
! 282:
! 283: buffer_init(&msg);
! 284: buffer_put_char(&msg, SSH2_FXP_OPENDIR);
! 285: buffer_put_int(&msg, id);
! 286: buffer_put_cstring(&msg, path);
! 287: send_msg(fd_out, &msg);
! 288:
! 289: buffer_clear(&msg);
! 290:
! 291: handle = get_handle(fd_in, id, &handle_len);
! 292: if (handle == NULL)
! 293: return(-1);
! 294:
! 295: for(;;) {
! 296: int count;
! 297:
! 298: expected_id = ++id;
! 299:
! 300: debug3("Sending SSH2_FXP_READDIR I:%d", id);
! 301:
! 302: buffer_clear(&msg);
! 303: buffer_put_char(&msg, SSH2_FXP_READDIR);
! 304: buffer_put_int(&msg, id);
! 305: buffer_put_string(&msg, handle, handle_len);
! 306: send_msg(fd_out, &msg);
! 307:
! 308: buffer_clear(&msg);
! 309:
! 310: get_msg(fd_in, &msg);
! 311:
! 312: type = buffer_get_char(&msg);
! 313: id = buffer_get_int(&msg);
! 314:
! 315: debug3("Received reply T:%d I:%d", type, id);
! 316:
! 317: if (id != expected_id)
! 318: fatal("ID mismatch (%d != %d)", id, expected_id);
! 319:
! 320: if (type == SSH2_FXP_STATUS) {
! 321: int status = buffer_get_int(&msg);
! 322:
! 323: debug3("Received SSH2_FXP_STATUS %d", status);
! 324:
! 325: if (status == SSH2_FX_EOF) {
! 326: break;
! 327: } else {
! 328: error("Couldn't read directory: %s",
! 329: fx2txt(status));
! 330: do_close(fd_in, fd_out, handle, handle_len);
! 331: return(NULL);
! 332: }
! 333: } else if (type != SSH2_FXP_NAME)
! 334: fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
! 335: SSH2_FXP_NAME, type);
! 336:
! 337: count = buffer_get_int(&msg);
! 338: debug3("Received %i SSH2_FXP_NAME responses", count);
! 339: for(i = 0; i < count; i++) {
! 340: char *filename, *longname;
! 341: Attrib *a;
! 342:
! 343: filename = buffer_get_string(&msg, NULL);
! 344: longname = buffer_get_string(&msg, NULL);
! 345: a = decode_attrib(&msg);
! 346:
! 347: printf("%s\n", longname);
! 348:
! 349: xfree(filename);
! 350: xfree(longname);
! 351: }
! 352: }
! 353:
! 354: buffer_free(&msg);
! 355: do_close(fd_in, fd_out, handle, handle_len);
! 356: xfree(handle);
! 357:
! 358: return(0);
! 359: }
! 360:
! 361: int
! 362: do_rm(int fd_in, int fd_out, char *path)
! 363: {
! 364: u_int status, id;
! 365:
! 366: debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
! 367:
! 368: id = arc4random();
! 369: send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path));
! 370: status = get_status(fd_in, id);
! 371: if (status != SSH2_FX_OK)
! 372: error("Couldn't delete file: %s", fx2txt(status));
! 373: return(status);
! 374: }
! 375:
! 376: int
! 377: do_mkdir(int fd_in, int fd_out, char *path, Attrib *a)
! 378: {
! 379: u_int status, id;
! 380:
! 381: id = arc4random();
! 382: send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path,
! 383: strlen(path), a);
! 384:
! 385: status = get_status(fd_in, id);
! 386: if (status != SSH2_FX_OK)
! 387: error("Couldn't create directory: %s", fx2txt(status));
! 388:
! 389: return(status);
! 390: }
! 391:
! 392: int
! 393: do_rmdir(int fd_in, int fd_out, char *path)
! 394: {
! 395: u_int status, id;
! 396:
! 397: id = arc4random();
! 398: send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path));
! 399:
! 400: status = get_status(fd_in, id);
! 401: if (status != SSH2_FX_OK)
! 402: error("Couldn't remove directory: %s", fx2txt(status));
! 403:
! 404: return(status);
! 405: }
! 406:
! 407: Attrib *
! 408: do_stat(int fd_in, int fd_out, char *path)
! 409: {
! 410: u_int id;
! 411:
! 412: id = arc4random();
! 413: send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path));
! 414: return(get_decode_stat(fd_in, id));
! 415: }
! 416:
! 417: Attrib *
! 418: do_lstat(int fd_in, int fd_out, char *path)
! 419: {
! 420: u_int id;
! 421:
! 422: id = arc4random();
! 423: send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path));
! 424: return(get_decode_stat(fd_in, id));
! 425: }
! 426:
! 427: Attrib *
! 428: do_fstat(int fd_in, int fd_out, char *handle,
! 429: u_int handle_len)
! 430: {
! 431: u_int id;
! 432:
! 433: id = arc4random();
! 434: send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len);
! 435: return(get_decode_stat(fd_in, id));
! 436: }
! 437:
! 438: int
! 439: do_setstat(int fd_in, int fd_out, char *path, Attrib *a)
! 440: {
! 441: u_int status, id;
! 442:
! 443: id = arc4random();
! 444: send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path,
! 445: strlen(path), a);
! 446:
! 447: status = get_status(fd_in, id);
! 448: if (status != SSH2_FX_OK)
! 449: error("Couldn't setstat on \"%s\": %s", path,
! 450: fx2txt(status));
! 451:
! 452: return(status);
! 453: }
! 454:
! 455: int
! 456: do_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len,
! 457: Attrib *a)
! 458: {
! 459: u_int status, id;
! 460:
! 461: id = arc4random();
! 462: send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle,
! 463: handle_len, a);
! 464:
! 465: status = get_status(fd_in, id);
! 466: if (status != SSH2_FX_OK)
! 467: error("Couldn't fsetstat: %s", fx2txt(status));
! 468:
! 469: return(status);
! 470: }
! 471:
! 472: char *
! 473: do_realpath(int fd_in, int fd_out, char *path)
! 474: {
! 475: Buffer msg;
! 476: u_int type, expected_id, count, id;
! 477: char *filename, *longname;
! 478: Attrib *a;
! 479:
! 480: expected_id = id = arc4random();
! 481: send_string_request(fd_out, id, SSH2_FXP_REALPATH, path,
! 482: strlen(path));
! 483:
! 484: buffer_init(&msg);
! 485:
! 486: get_msg(fd_in, &msg);
! 487: type = buffer_get_char(&msg);
! 488: id = buffer_get_int(&msg);
! 489:
! 490: if (id != expected_id)
! 491: fatal("ID mismatch (%d != %d)", id, expected_id);
! 492:
! 493: if (type == SSH2_FXP_STATUS) {
! 494: u_int status = buffer_get_int(&msg);
! 495:
! 496: error("Couldn't canonicalise: %s", fx2txt(status));
! 497: return(NULL);
! 498: } else if (type != SSH2_FXP_NAME)
! 499: fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
! 500: SSH2_FXP_NAME, type);
! 501:
! 502: count = buffer_get_int(&msg);
! 503: if (count != 1)
! 504: fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
! 505:
! 506: filename = buffer_get_string(&msg, NULL);
! 507: longname = buffer_get_string(&msg, NULL);
! 508: a = decode_attrib(&msg);
! 509:
! 510: debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
! 511:
! 512: xfree(longname);
! 513:
! 514: buffer_free(&msg);
! 515:
! 516: return(filename);
! 517: }
! 518:
! 519: int
! 520: do_rename(int fd_in, int fd_out, char *oldpath, char *newpath)
! 521: {
! 522: Buffer msg;
! 523: u_int status, id;
! 524:
! 525: buffer_init(&msg);
! 526:
! 527: /* Send rename request */
! 528: id = arc4random();
! 529: buffer_put_char(&msg, SSH2_FXP_RENAME);
! 530: buffer_put_int(&msg, id);
! 531: buffer_put_cstring(&msg, oldpath);
! 532: buffer_put_cstring(&msg, newpath);
! 533: send_msg(fd_out, &msg);
! 534: debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
! 535: newpath);
! 536: buffer_free(&msg);
! 537:
! 538: status = get_status(fd_in, id);
! 539: if (status != SSH2_FX_OK)
! 540: error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath,
! 541: fx2txt(status));
! 542:
! 543: return(status);
! 544: }
! 545:
! 546: int
! 547: do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
! 548: int pflag)
! 549: {
! 550: int local_fd;
! 551: u_int expected_id, handle_len, mode, type, id;
! 552: u_int64_t offset;
! 553: char *handle;
! 554: Buffer msg;
! 555: Attrib junk, *a;
! 556:
! 557: a = do_stat(fd_in, fd_out, remote_path);
! 558: if (a == NULL)
! 559: return(-1);
! 560:
! 561: /* XXX: should we preserve set[ug]id? */
! 562: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
! 563: mode = S_IWRITE | (a->perm & 0777);
! 564: else
! 565: mode = 0666;
! 566:
! 567: local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
! 568: if (local_fd == -1) {
! 569: error("Couldn't open local file \"%s\" for writing: %s",
! 570: local_path, strerror(errno));
! 571: return(errno);
! 572: }
! 573:
! 574: /* Override umask and utimes if asked */
! 575: if (pflag && fchmod(local_fd, mode) == -1)
! 576: error("Couldn't set mode on \"%s\": %s", local_path,
! 577: strerror(errno));
! 578: if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
! 579: struct timeval tv;
! 580:
! 581: tv.tv_sec = a->atime;
! 582: tv.tv_usec = a->mtime;
! 583: if (utimes(local_path, &tv) == -1)
! 584: error("Can't set times on \"%s\": %s", local_path,
! 585: strerror(errno));
! 586: }
! 587:
! 588: buffer_init(&msg);
! 589:
! 590: /* Send open request */
! 591: id = arc4random();
! 592: buffer_put_char(&msg, SSH2_FXP_OPEN);
! 593: buffer_put_int(&msg, id);
! 594: buffer_put_cstring(&msg, remote_path);
! 595: buffer_put_int(&msg, SSH2_FXF_READ);
! 596: attrib_clear(&junk); /* Send empty attributes */
! 597: encode_attrib(&msg, &junk);
! 598: send_msg(fd_out, &msg);
! 599: debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
! 600:
! 601: handle = get_handle(fd_in, id, &handle_len);
! 602: if (handle == NULL) {
! 603: buffer_free(&msg);
! 604: close(local_fd);
! 605: return(-1);
! 606: }
! 607:
! 608: /* Read from remote and write to local */
! 609: offset = 0;
! 610: for(;;) {
! 611: u_int len;
! 612: char *data;
! 613:
! 614: expected_id = ++id;
! 615:
! 616: buffer_clear(&msg);
! 617: buffer_put_char(&msg, SSH2_FXP_READ);
! 618: buffer_put_int(&msg, id);
! 619: buffer_put_string(&msg, handle, handle_len);
! 620: buffer_put_int64(&msg, offset);
! 621: buffer_put_int(&msg, COPY_SIZE);
! 622: send_msg(fd_out, &msg);
! 623: debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u",
! 624: id, offset, COPY_SIZE);
! 625:
! 626: buffer_clear(&msg);
! 627:
! 628: get_msg(fd_in, &msg);
! 629: type = buffer_get_char(&msg);
! 630: id = buffer_get_int(&msg);
! 631: debug3("Received reply T:%d I:%d", type, id);
! 632: if (id != expected_id)
! 633: fatal("ID mismatch (%d != %d)", id, expected_id);
! 634: if (type == SSH2_FXP_STATUS) {
! 635: int status = buffer_get_int(&msg);
! 636:
! 637: if (status == SSH2_FX_EOF)
! 638: break;
! 639: else {
! 640: error("Couldn't read from remote "
! 641: "file \"%s\" : %s", remote_path,
! 642: fx2txt(status));
! 643: do_close(fd_in, fd_out, handle, handle_len);
! 644: xfree(handle);
! 645: close(local_fd);
! 646: buffer_free(&msg);
! 647: return(status);
! 648: }
! 649: } else if (type != SSH2_FXP_DATA) {
! 650: fatal("Expected SSH2_FXP_DATA(%d) packet, got %d",
! 651: SSH2_FXP_DATA, type);
! 652: }
! 653:
! 654: data = buffer_get_string(&msg, &len);
! 655: if (len > COPY_SIZE)
! 656: fatal("Received more data than asked for %d > %d",
! 657: len, COPY_SIZE);
! 658:
! 659: debug3("In read loop, got %d offset %lld", len, offset);
! 660: if (atomicio(write, local_fd, data, len) != len) {
! 661: error("Couldn't write to \"%s\": %s", local_path,
! 662: strerror(errno));
! 663: do_close(fd_in, fd_out, handle, handle_len);
! 664: xfree(handle);
! 665: close(local_fd);
! 666: xfree(data);
! 667: buffer_free(&msg);
! 668: return(-1);
! 669: }
! 670:
! 671: offset += len;
! 672: xfree(data);
! 673: }
! 674: xfree(handle);
! 675: buffer_free(&msg);
! 676: close(local_fd);
! 677:
! 678: return(do_close(fd_in, fd_out, handle, handle_len));
! 679: }
! 680:
! 681: int
! 682: do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
! 683: int pflag)
! 684: {
! 685: int local_fd;
! 686: u_int handle_len, id;
! 687: u_int64_t offset;
! 688: char *handle;
! 689: Buffer msg;
! 690: struct stat sb;
! 691: Attrib a;
! 692:
! 693: if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
! 694: error("Couldn't open local file \"%s\" for reading: %s",
! 695: local_path, strerror(errno));
! 696: return(-1);
! 697: }
! 698: if (fstat(local_fd, &sb) == -1) {
! 699: error("Couldn't fstat local file \"%s\": %s",
! 700: local_path, strerror(errno));
! 701: close(local_fd);
! 702: return(-1);
! 703: }
! 704: stat_to_attrib(&sb, &a);
! 705:
! 706: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
! 707: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
! 708: a.perm &= 0777;
! 709: if (!pflag)
! 710: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
! 711:
! 712: buffer_init(&msg);
! 713:
! 714: /* Send open request */
! 715: id = arc4random();
! 716: buffer_put_char(&msg, SSH2_FXP_OPEN);
! 717: buffer_put_int(&msg, id);
! 718: buffer_put_cstring(&msg, remote_path);
! 719: buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
! 720: encode_attrib(&msg, &a);
! 721: send_msg(fd_out, &msg);
! 722: debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
! 723:
! 724: buffer_clear(&msg);
! 725:
! 726: handle = get_handle(fd_in, id, &handle_len);
! 727: if (handle == NULL) {
! 728: close(local_fd);
! 729: buffer_free(&msg);
! 730: return(-1);
! 731: }
! 732:
! 733: /* Override umask and utimes if asked */
! 734: if (pflag)
! 735: do_fsetstat(fd_in, fd_out, handle, handle_len, &a);
! 736:
! 737: /* Read from local and write to remote */
! 738: offset = 0;
! 739: for(;;) {
! 740: int len;
! 741: char data[COPY_SIZE];
! 742: u_int status;
! 743:
! 744: /*
! 745: * Can't use atomicio here because it returns 0 on EOF, thus losing
! 746: * the last block of the file
! 747: */
! 748: do
! 749: len = read(local_fd, data, COPY_SIZE);
! 750: while ((len == -1) && (errno == EINTR || errno == EAGAIN));
! 751:
! 752: if (len == -1)
! 753: fatal("Couldn't read from \"%s\": %s", local_path,
! 754: strerror(errno));
! 755: if (len == 0)
! 756: break;
! 757:
! 758: buffer_clear(&msg);
! 759: buffer_put_char(&msg, SSH2_FXP_WRITE);
! 760: buffer_put_int(&msg, ++id);
! 761: buffer_put_string(&msg, handle, handle_len);
! 762: buffer_put_int64(&msg, offset);
! 763: buffer_put_string(&msg, data, len);
! 764: send_msg(fd_out, &msg);
! 765: debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
! 766: id, offset, len);
! 767:
! 768: status = get_status(fd_in, id);
! 769: if (status != SSH2_FX_OK) {
! 770: error("Couldn't write to remote file \"%s\": %s",
! 771: remote_path, fx2txt(status));
! 772: do_close(fd_in, fd_out, handle, handle_len);
! 773: xfree(handle);
! 774: close(local_fd);
! 775: return(-1);
! 776: }
! 777: debug3("In write loop, got %d offset %lld", len, offset);
! 778:
! 779: offset += len;
! 780: }
! 781: xfree(handle);
! 782: buffer_free(&msg);
! 783:
! 784: if (close(local_fd) == -1) {
! 785: error("Couldn't close local file \"%s\": %s", local_path,
! 786: strerror(errno));
! 787: do_close(fd_in, fd_out, handle, handle_len);
! 788: return(-1);
! 789: }
! 790:
! 791: return(do_close(fd_in, fd_out, handle, handle_len));
! 792: }