sftp-client.c revision 137015
162642Sn_hibma/* 262642Sn_hibma * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 362642Sn_hibma * 462642Sn_hibma * Permission to use, copy, modify, and distribute this software for any 562642Sn_hibma * purpose with or without fee is hereby granted, provided that the above 662642Sn_hibma * copyright notice and this permission notice appear in all copies. 762642Sn_hibma * 862642Sn_hibma * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962642Sn_hibma * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062642Sn_hibma * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162642Sn_hibma * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262642Sn_hibma * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362642Sn_hibma * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462642Sn_hibma * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562642Sn_hibma */ 1662642Sn_hibma 1762642Sn_hibma/* XXX: memleaks */ 1862642Sn_hibma/* XXX: signed vs unsigned */ 1962642Sn_hibma/* XXX: remove all logging, only return status codes */ 2062642Sn_hibma/* XXX: copy between two remote sites */ 2162642Sn_hibma 2262642Sn_hibma#include "includes.h" 2362642Sn_hibmaRCSID("$OpenBSD: sftp-client.c,v 1.51 2004/07/11 17:48:47 deraadt Exp $"); 2462642Sn_hibma 2562642Sn_hibma#include "openbsd-compat/sys-queue.h" 2662642Sn_hibma 2762642Sn_hibma#include "buffer.h" 2862642Sn_hibma#include "bufaux.h" 2962642Sn_hibma#include "getput.h" 3062642Sn_hibma#include "xmalloc.h" 3162642Sn_hibma#include "log.h" 3262642Sn_hibma#include "atomicio.h" 3362642Sn_hibma#include "progressmeter.h" 3462642Sn_hibma 3562642Sn_hibma#include "sftp.h" 3662642Sn_hibma#include "sftp-common.h" 3762642Sn_hibma#include "sftp-client.h" 3862642Sn_hibma 3962642Sn_hibmaextern volatile sig_atomic_t interrupted; 4062642Sn_hibmaextern int showprogress; 4162642Sn_hibma 42113273Smdodd/* Minimum amount of data to read at at time */ 43188945Sthompsa#define MIN_READ_SIZE 512 4462642Sn_hibma 45227196Sed/* Maximum packet size */ 46225839Smav#define MAX_MSG_LENGTH (256 * 1024) 47225839Smav 48225839Smavstruct sftp_conn { 49225839Smav int fd_in; 50225839Smav int fd_out; 51225839Smav u_int transfer_buflen; 52225839Smav u_int num_requests; 53227196Sed u_int version; 54227196Sed u_int msg_id; 55227196Sed}; 56227196Sed 57227196Sedstatic void 5862642Sn_hibmasend_msg(int fd, Buffer *m) 59225839Smav{ 60225839Smav u_char mlen[4]; 61225839Smav 62225839Smav if (buffer_len(m) > MAX_MSG_LENGTH) 63225839Smav fatal("Outbound message too long %u", buffer_len(m)); 64225839Smav 6562642Sn_hibma /* Send length first */ 66225839Smav PUT_32BIT(mlen, buffer_len(m)); 67225839Smav if (atomicio(vwrite, fd, mlen, sizeof(mlen)) <= 0) 6862642Sn_hibma fatal("Couldn't send packet: %s", strerror(errno)); 69225839Smav 70225839Smav if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) <= 0) 71225839Smav fatal("Couldn't send packet: %s", strerror(errno)); 72225839Smav 73225839Smav buffer_clear(m); 74225839Smav} 7562642Sn_hibma 76225839Smavstatic void 77225839Smavget_msg(int fd, Buffer *m) 78225839Smav{ 79225839Smav ssize_t len; 80225839Smav u_int msg_len; 81225839Smav 82225839Smav buffer_append_space(m, 4); 83225839Smav len = atomicio(read, fd, buffer_ptr(m), 4); 84225839Smav if (len == 0) 85225839Smav fatal("Connection closed"); 86225839Smav else if (len == -1) 87225839Smav fatal("Couldn't read packet: %s", strerror(errno)); 88225839Smav 89225839Smav msg_len = buffer_get_int(m); 90225839Smav if (msg_len > MAX_MSG_LENGTH) 91225839Smav fatal("Received message too long %u", msg_len); 92225839Smav 93225839Smav buffer_append_space(m, msg_len); 94225839Smav len = atomicio(read, fd, buffer_ptr(m), msg_len); 95225839Smav if (len == 0) 96225839Smav fatal("Connection closed"); 97225839Smav else if (len == -1) 98225839Smav fatal("Read packet: %s", strerror(errno)); 99225839Smav} 100225839Smav 101225839Smavstatic void 102225839Smavsend_string_request(int fd, u_int id, u_int code, char *s, 103225839Smav u_int len) 104225839Smav{ 105225839Smav Buffer msg; 106225839Smav 107225839Smav buffer_init(&msg); 108225839Smav buffer_put_char(&msg, code); 109225839Smav buffer_put_int(&msg, id); 110225839Smav buffer_put_string(&msg, s, len); 111225839Smav send_msg(fd, &msg); 112225839Smav debug3("Sent message fd %d T:%u I:%u", fd, code, id); 113225839Smav buffer_free(&msg); 114225839Smav} 115225839Smav 116225839Smavstatic void 117225839Smavsend_string_attrs_request(int fd, u_int id, u_int code, char *s, 118225839Smav u_int len, Attrib *a) 119225839Smav{ 120225839Smav Buffer msg; 121225839Smav 122225839Smav buffer_init(&msg); 123225839Smav buffer_put_char(&msg, code); 124225839Smav buffer_put_int(&msg, id); 125225839Smav buffer_put_string(&msg, s, len); 126225839Smav encode_attrib(&msg, a); 127225839Smav send_msg(fd, &msg); 128225839Smav debug3("Sent message fd %d T:%u I:%u", fd, code, id); 129225839Smav buffer_free(&msg); 130225839Smav} 131225839Smav 13262642Sn_hibmastatic u_int 133225839Smavget_status(int fd, u_int expected_id) 134225839Smav{ 135225839Smav Buffer msg; 136225839Smav u_int type, id, status; 137225839Smav 138225839Smav buffer_init(&msg); 139225839Smav get_msg(fd, &msg); 140225839Smav type = buffer_get_char(&msg); 141225839Smav id = buffer_get_int(&msg); 142225839Smav 143225839Smav if (id != expected_id) 144225839Smav fatal("ID mismatch (%u != %u)", id, expected_id); 145225839Smav if (type != SSH2_FXP_STATUS) 146225839Smav fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", 147225839Smav SSH2_FXP_STATUS, type); 148225839Smav 149225839Smav status = buffer_get_int(&msg); 150225839Smav buffer_free(&msg); 151225839Smav 152225839Smav debug3("SSH2_FXP_STATUS %u", status); 153225839Smav 154225839Smav return(status); 155225839Smav} 156225839Smav 157225839Smavstatic char * 158225839Smavget_handle(int fd, u_int expected_id, u_int *len) 159225839Smav{ 160225839Smav Buffer msg; 161225839Smav u_int type, id; 162225839Smav char *handle; 163225839Smav 164225839Smav buffer_init(&msg); 165225839Smav get_msg(fd, &msg); 166225839Smav type = buffer_get_char(&msg); 167225839Smav id = buffer_get_int(&msg); 168225839Smav 169225839Smav if (id != expected_id) 170225839Smav fatal("ID mismatch (%u != %u)", id, expected_id); 171225839Smav if (type == SSH2_FXP_STATUS) { 172225839Smav int status = buffer_get_int(&msg); 173225839Smav 174225839Smav error("Couldn't get handle: %s", fx2txt(status)); 175225839Smav return(NULL); 176225839Smav } else if (type != SSH2_FXP_HANDLE) 17762642Sn_hibma fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u", 17862642Sn_hibma SSH2_FXP_HANDLE, type); 179225839Smav 18062642Sn_hibma handle = buffer_get_string(&msg, len); 18162642Sn_hibma buffer_free(&msg); 18262642Sn_hibma 183164531Sgrog return(handle); 184164531Sgrog} 185235519Smav 186194789Sdelphijstatic Attrib * 187164531Sgrogget_decode_stat(int fd, u_int expected_id, int quiet) 188164531Sgrog{ 189235519Smav Buffer msg; 190194789Sdelphij u_int type, id; 191225839Smav Attrib *a; 192225839Smav 193225839Smav buffer_init(&msg); 194225839Smav get_msg(fd, &msg); 19562642Sn_hibma 19662642Sn_hibma type = buffer_get_char(&msg); 19762642Sn_hibma id = buffer_get_int(&msg); 198225839Smav 19987699Smarkm debug3("Received stat reply T:%u I:%u", type, id); 20062642Sn_hibma if (id != expected_id) 20162642Sn_hibma fatal("ID mismatch (%u != %u)", id, expected_id); 20262642Sn_hibma if (type == SSH2_FXP_STATUS) { 203224511Smav int status = buffer_get_int(&msg); 204224511Smav 205164531Sgrog if (quiet) 20662642Sn_hibma debug("Couldn't stat remote file: %s", fx2txt(status)); 207224511Smav else 208224511Smav error("Couldn't stat remote file: %s", fx2txt(status)); 20962642Sn_hibma return(NULL); 21062642Sn_hibma } else if (type != SSH2_FXP_ATTRS) { 21162642Sn_hibma fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", 21262642Sn_hibma SSH2_FXP_ATTRS, type); 21362642Sn_hibma } 21462642Sn_hibma a = decode_attrib(&msg); 21562642Sn_hibma buffer_free(&msg); 21662642Sn_hibma 21762642Sn_hibma return(a); 21862642Sn_hibma} 219224511Smav 220224511Smavstruct sftp_conn * 221224511Smavdo_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) 222224511Smav{ 223224511Smav u_int type; 224224511Smav int version; 225224511Smav Buffer msg; 226224511Smav struct sftp_conn *ret; 227224511Smav 228224511Smav buffer_init(&msg); 229224511Smav buffer_put_char(&msg, SSH2_FXP_INIT); 230224511Smav buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 231224511Smav send_msg(fd_out, &msg); 232224511Smav 233224511Smav buffer_clear(&msg); 234224511Smav 235224511Smav get_msg(fd_in, &msg); 236224511Smav 237225839Smav /* Expecting a VERSION reply */ 23862642Sn_hibma if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { 23962642Sn_hibma error("Invalid packet back from SSH2_FXP_INIT (type %u)", 24062642Sn_hibma type); 24162642Sn_hibma buffer_free(&msg); 24267256Sn_hibma return(NULL); 24362642Sn_hibma } 244224511Smav version = buffer_get_int(&msg); 24562642Sn_hibma 24662642Sn_hibma debug2("Remote version: %d", version); 247224511Smav 248224511Smav /* Check for extensions */ 249164531Sgrog while (buffer_len(&msg) > 0) { 25062642Sn_hibma char *name = buffer_get_string(&msg, NULL); 25162642Sn_hibma char *value = buffer_get_string(&msg, NULL); 25262642Sn_hibma 25362642Sn_hibma debug2("Init extension: \"%s\"", name); 25462642Sn_hibma xfree(name); 25562642Sn_hibma xfree(value); 25662642Sn_hibma } 25762642Sn_hibma 25862642Sn_hibma buffer_free(&msg); 25962642Sn_hibma 26062642Sn_hibma ret = xmalloc(sizeof(*ret)); 26162642Sn_hibma ret->fd_in = fd_in; 26262642Sn_hibma ret->fd_out = fd_out; 26362642Sn_hibma ret->transfer_buflen = transfer_buflen; 26462642Sn_hibma ret->num_requests = num_requests; 26562642Sn_hibma ret->version = version; 26662642Sn_hibma ret->msg_id = 1; 267224511Smav 26875606Sn_hibma /* Some filexfer v.0 servers don't support large packets */ 26962642Sn_hibma if (version == 0) 270224511Smav ret->transfer_buflen = MIN(ret->transfer_buflen, 20480); 27167256Sn_hibma 27267256Sn_hibma return(ret); 273224511Smav} 27467256Sn_hibma 27562642Sn_hibmau_int 27662642Sn_hibmasftp_proto_version(struct sftp_conn *conn) 277225839Smav{ 27862642Sn_hibma return(conn->version); 27962642Sn_hibma} 28062642Sn_hibma 28162642Sn_hibmaint 28262642Sn_hibmado_close(struct sftp_conn *conn, char *handle, u_int handle_len) 28362642Sn_hibma{ 28462642Sn_hibma u_int id, status; 28562642Sn_hibma Buffer msg; 286224511Smav 287224511Smav buffer_init(&msg); 28862642Sn_hibma 28962642Sn_hibma id = conn->msg_id++; 29062642Sn_hibma buffer_put_char(&msg, SSH2_FXP_CLOSE); 29162642Sn_hibma buffer_put_int(&msg, id); 292164531Sgrog buffer_put_string(&msg, handle, handle_len); 293164531Sgrog send_msg(conn->fd_out, &msg); 294224511Smav debug3("Sent message SSH2_FXP_CLOSE I:%u", id); 29562642Sn_hibma 296224511Smav status = get_status(conn->fd_in, id); 29762642Sn_hibma if (status != SSH2_FX_OK) 29862642Sn_hibma error("Couldn't close file: %s", fx2txt(status)); 299225839Smav 30062642Sn_hibma buffer_free(&msg); 30162642Sn_hibma 302225839Smav return(status); 303225839Smav} 30462642Sn_hibma 305225839Smav 30662642Sn_hibmastatic int 307235519Smavdo_lsreaddir(struct sftp_conn *conn, char *path, int printflag, 308225839Smav SFTP_DIRENT ***dir) 309225839Smav{ 310225839Smav Buffer msg; 311225839Smav u_int type, id, handle_len, i, expected_id, ents = 0; 312225839Smav char *handle; 313225839Smav 314225839Smav id = conn->msg_id++; 315225839Smav 316225839Smav buffer_init(&msg); 317225839Smav buffer_put_char(&msg, SSH2_FXP_OPENDIR); 318225839Smav buffer_put_int(&msg, id); 319225839Smav buffer_put_cstring(&msg, path); 320225839Smav send_msg(conn->fd_out, &msg); 321225839Smav 322225839Smav buffer_clear(&msg); 323225839Smav 324225839Smav handle = get_handle(conn->fd_in, id, &handle_len); 325225839Smav if (handle == NULL) 326225839Smav return(-1); 327225839Smav 32862642Sn_hibma if (dir) { 329225839Smav ents = 0; 330225839Smav *dir = xmalloc(sizeof(**dir)); 331225839Smav (*dir)[0] = NULL; 332225839Smav } 333225839Smav 334225839Smav for (; !interrupted;) { 335225839Smav int count; 336225839Smav 337225839Smav id = expected_id = conn->msg_id++; 338225839Smav 339225839Smav debug3("Sending SSH2_FXP_READDIR I:%u", id); 340225839Smav 341225839Smav buffer_clear(&msg); 342225839Smav buffer_put_char(&msg, SSH2_FXP_READDIR); 343225839Smav buffer_put_int(&msg, id); 34462642Sn_hibma buffer_put_string(&msg, handle, handle_len); 345225839Smav send_msg(conn->fd_out, &msg); 346225839Smav 347225839Smav buffer_clear(&msg); 348225839Smav 349225839Smav get_msg(conn->fd_in, &msg); 350225839Smav 351225839Smav type = buffer_get_char(&msg); 352225839Smav id = buffer_get_int(&msg); 353225839Smav 354225839Smav debug3("Received reply T:%u I:%u", type, id); 35562642Sn_hibma 356225839Smav if (id != expected_id) 357225839Smav fatal("ID mismatch (%u != %u)", id, expected_id); 358225839Smav 359224511Smav if (type == SSH2_FXP_STATUS) { 360225839Smav int status = buffer_get_int(&msg); 361225839Smav 362225839Smav debug3("Received SSH2_FXP_STATUS %d", status); 363225839Smav 364225839Smav if (status == SSH2_FX_EOF) { 365225839Smav break; 366225839Smav } else { 367225839Smav error("Couldn't read directory: %s", 368225839Smav fx2txt(status)); 369225839Smav do_close(conn, handle, handle_len); 370225839Smav xfree(handle); 371225839Smav return(status); 372225839Smav } 373225839Smav } else if (type != SSH2_FXP_NAME) 374225839Smav fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 375225839Smav SSH2_FXP_NAME, type); 376225839Smav 377225839Smav count = buffer_get_int(&msg); 378225839Smav if (count == 0) 379225839Smav break; 380225839Smav debug3("Received %d SSH2_FXP_NAME responses", count); 381225839Smav for (i = 0; i < count; i++) { 382225839Smav char *filename, *longname; 383225839Smav Attrib *a; 384225839Smav 385225839Smav filename = buffer_get_string(&msg, NULL); 386225839Smav longname = buffer_get_string(&msg, NULL); 387225839Smav a = decode_attrib(&msg); 388225839Smav 389225839Smav if (printflag) 390225839Smav printf("%s\n", longname); 391225839Smav 392225839Smav if (dir) { 393225839Smav *dir = xrealloc(*dir, sizeof(**dir) * 394225839Smav (ents + 2)); 395225839Smav (*dir)[ents] = xmalloc(sizeof(***dir)); 396225839Smav (*dir)[ents]->filename = xstrdup(filename); 397225839Smav (*dir)[ents]->longname = xstrdup(longname); 398225839Smav memcpy(&(*dir)[ents]->a, a, sizeof(*a)); 399225839Smav (*dir)[++ents] = NULL; 400225839Smav } 401225839Smav 402225839Smav xfree(filename); 403225839Smav xfree(longname); 404225839Smav } 405225839Smav } 406225839Smav 407225839Smav buffer_free(&msg); 40862642Sn_hibma do_close(conn, handle, handle_len); 40962642Sn_hibma xfree(handle); 410225839Smav 411225839Smav /* Don't return partial matches on interrupt */ 412225839Smav if (interrupted && dir != NULL && *dir != NULL) { 413225839Smav free_sftp_dirents(*dir); 414225839Smav *dir = xmalloc(sizeof(**dir)); 415225839Smav **dir = NULL; 416225839Smav } 417225839Smav 418225839Smav return(0); 41962642Sn_hibma} 420225839Smav 421225839Smavint 422225839Smavdo_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir) 423225839Smav{ 424225839Smav return(do_lsreaddir(conn, path, 0, dir)); 425225839Smav} 426225839Smav 427225839Smavvoid free_sftp_dirents(SFTP_DIRENT **s) 428225839Smav{ 429225839Smav int i; 430225839Smav 431225839Smav for (i = 0; s[i]; i++) { 43262642Sn_hibma xfree(s[i]->filename); 433225839Smav xfree(s[i]->longname); 434225839Smav xfree(s[i]); 435225839Smav } 436225839Smav xfree(s); 437225839Smav} 438225839Smav 439225839Smavint 44062642Sn_hibmado_rm(struct sftp_conn *conn, char *path) 44162642Sn_hibma{ 44262642Sn_hibma u_int status, id; 44362642Sn_hibma 44462642Sn_hibma debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); 445225839Smav 446225839Smav id = conn->msg_id++; 447225839Smav send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path, 44862642Sn_hibma strlen(path)); 449225839Smav status = get_status(conn->fd_in, id); 45062642Sn_hibma if (status != SSH2_FX_OK) 45162642Sn_hibma error("Couldn't delete file: %s", fx2txt(status)); 45262642Sn_hibma return(status); 45362642Sn_hibma} 454225839Smav 45562642Sn_hibmaint 45662642Sn_hibmado_mkdir(struct sftp_conn *conn, char *path, Attrib *a) 45762642Sn_hibma{ 45862642Sn_hibma u_int status, id; 45962642Sn_hibma 46062642Sn_hibma id = conn->msg_id++; 46162642Sn_hibma send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path, 46262642Sn_hibma strlen(path), a); 46362642Sn_hibma 46462642Sn_hibma status = get_status(conn->fd_in, id); 46562642Sn_hibma if (status != SSH2_FX_OK) 46662642Sn_hibma error("Couldn't create directory: %s", fx2txt(status)); 46762642Sn_hibma 46862642Sn_hibma return(status); 46962642Sn_hibma} 47062642Sn_hibma 47162642Sn_hibmaint 47262642Sn_hibmado_rmdir(struct sftp_conn *conn, char *path) 47362642Sn_hibma{ 47462642Sn_hibma u_int status, id; 47562642Sn_hibma 47662642Sn_hibma id = conn->msg_id++; 477225839Smav send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path, 478225839Smav strlen(path)); 479225839Smav 480164531Sgrog status = get_status(conn->fd_in, id); 481164544Sgrog if (status != SSH2_FX_OK) 482164531Sgrog error("Couldn't remove directory: %s", fx2txt(status)); 483225839Smav 484225839Smav return(status); 485225839Smav} 48662642Sn_hibma 48762642Sn_hibmaAttrib * 48862642Sn_hibmado_stat(struct sftp_conn *conn, char *path, int quiet) 48962642Sn_hibma{ 49062642Sn_hibma u_int id; 49162642Sn_hibma 49262642Sn_hibma id = conn->msg_id++; 493225839Smav 49462642Sn_hibma send_string_request(conn->fd_out, id, 49562642Sn_hibma conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, 496225839Smav path, strlen(path)); 49762642Sn_hibma 49862642Sn_hibma return(get_decode_stat(conn->fd_in, id, quiet)); 49962642Sn_hibma} 50062642Sn_hibma 50187699SmarkmAttrib * 50262642Sn_hibmado_lstat(struct sftp_conn *conn, char *path, int quiet) 50387699Smarkm{ 50487699Smarkm u_int id; 50562642Sn_hibma 50662642Sn_hibma if (conn->version == 0) { 50762642Sn_hibma if (quiet) 50862642Sn_hibma debug("Server version does not support lstat operation"); 50962642Sn_hibma else 51062642Sn_hibma logit("Server version does not support lstat operation"); 51162642Sn_hibma return(do_stat(conn, path, quiet)); 51262642Sn_hibma } 51362642Sn_hibma 514164531Sgrog id = conn->msg_id++; 51562642Sn_hibma send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path, 516164531Sgrog strlen(path)); 51762642Sn_hibma 51862642Sn_hibma return(get_decode_stat(conn->fd_in, id, quiet)); 51962642Sn_hibma} 52062642Sn_hibma 521225839SmavAttrib * 522225839Smavdo_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) 523225839Smav{ 524225839Smav u_int id; 525225839Smav 526225839Smav id = conn->msg_id++; 527225839Smav send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle, 52862642Sn_hibma handle_len); 52962642Sn_hibma 53062642Sn_hibma return(get_decode_stat(conn->fd_in, id, quiet)); 53162642Sn_hibma} 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 symlink 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 && size != 0) 817 start_progress_meter(remote_path, size, &progress_counter); 818 819 while (num_req > 0 || max_req > 0) { 820 char *data; 821 u_int len; 822 823 /* 824 * Simulate EOF on interrupt: stop sending new requests and 825 * allow outstanding requests to drain gracefully 826 */ 827 if (interrupted) { 828 if (num_req == 0) /* If we haven't started yet... */ 829 break; 830 max_req = 0; 831 } 832 833 /* Send some more requests */ 834 while (num_req < max_req) { 835 debug3("Request range %llu -> %llu (%d/%d)", 836 (unsigned long long)offset, 837 (unsigned long long)offset + buflen - 1, 838 num_req, max_req); 839 req = xmalloc(sizeof(*req)); 840 req->id = conn->msg_id++; 841 req->len = buflen; 842 req->offset = offset; 843 offset += buflen; 844 num_req++; 845 TAILQ_INSERT_TAIL(&requests, req, tq); 846 send_read_request(conn->fd_out, req->id, req->offset, 847 req->len, handle, handle_len); 848 } 849 850 buffer_clear(&msg); 851 get_msg(conn->fd_in, &msg); 852 type = buffer_get_char(&msg); 853 id = buffer_get_int(&msg); 854 debug3("Received reply T:%u I:%u R:%d", type, id, max_req); 855 856 /* Find the request in our queue */ 857 for(req = TAILQ_FIRST(&requests); 858 req != NULL && req->id != id; 859 req = TAILQ_NEXT(req, tq)) 860 ; 861 if (req == NULL) 862 fatal("Unexpected reply %u", id); 863 864 switch (type) { 865 case SSH2_FXP_STATUS: 866 status = buffer_get_int(&msg); 867 if (status != SSH2_FX_EOF) 868 read_error = 1; 869 max_req = 0; 870 TAILQ_REMOVE(&requests, req, tq); 871 xfree(req); 872 num_req--; 873 break; 874 case SSH2_FXP_DATA: 875 data = buffer_get_string(&msg, &len); 876 debug3("Received data %llu -> %llu", 877 (unsigned long long)req->offset, 878 (unsigned long long)req->offset + len - 1); 879 if (len > req->len) 880 fatal("Received more data than asked for " 881 "%u > %u", len, req->len); 882 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || 883 atomicio(vwrite, local_fd, data, len) != len) && 884 !write_error) { 885 write_errno = errno; 886 write_error = 1; 887 max_req = 0; 888 } 889 progress_counter += len; 890 xfree(data); 891 892 if (len == req->len) { 893 TAILQ_REMOVE(&requests, req, tq); 894 xfree(req); 895 num_req--; 896 } else { 897 /* Resend the request for the missing data */ 898 debug3("Short data block, re-requesting " 899 "%llu -> %llu (%2d)", 900 (unsigned long long)req->offset + len, 901 (unsigned long long)req->offset + 902 req->len - 1, num_req); 903 req->id = conn->msg_id++; 904 req->len -= len; 905 req->offset += len; 906 send_read_request(conn->fd_out, req->id, 907 req->offset, req->len, handle, handle_len); 908 /* Reduce the request size */ 909 if (len < buflen) 910 buflen = MAX(MIN_READ_SIZE, len); 911 } 912 if (max_req > 0) { /* max_req = 0 iff EOF received */ 913 if (size > 0 && offset > size) { 914 /* Only one request at a time 915 * after the expected EOF */ 916 debug3("Finish at %llu (%2d)", 917 (unsigned long long)offset, 918 num_req); 919 max_req = 1; 920 } else if (max_req <= conn->num_requests) { 921 ++max_req; 922 } 923 } 924 break; 925 default: 926 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", 927 SSH2_FXP_DATA, type); 928 } 929 } 930 931 if (showprogress && size) 932 stop_progress_meter(); 933 934 /* Sanity check */ 935 if (TAILQ_FIRST(&requests) != NULL) 936 fatal("Transfer complete, but requests still in queue"); 937 938 if (read_error) { 939 error("Couldn't read from remote file \"%s\" : %s", 940 remote_path, fx2txt(status)); 941 do_close(conn, handle, handle_len); 942 } else if (write_error) { 943 error("Couldn't write to \"%s\": %s", local_path, 944 strerror(write_errno)); 945 status = -1; 946 do_close(conn, handle, handle_len); 947 } else { 948 status = do_close(conn, handle, handle_len); 949 950 /* Override umask and utimes if asked */ 951#ifdef HAVE_FCHMOD 952 if (pflag && fchmod(local_fd, mode) == -1) 953#else 954 if (pflag && chmod(local_path, mode) == -1) 955#endif /* HAVE_FCHMOD */ 956 error("Couldn't set mode on \"%s\": %s", local_path, 957 strerror(errno)); 958 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { 959 struct timeval tv[2]; 960 tv[0].tv_sec = a->atime; 961 tv[1].tv_sec = a->mtime; 962 tv[0].tv_usec = tv[1].tv_usec = 0; 963 if (utimes(local_path, tv) == -1) 964 error("Can't set times on \"%s\": %s", 965 local_path, strerror(errno)); 966 } 967 } 968 close(local_fd); 969 buffer_free(&msg); 970 xfree(handle); 971 972 return(status); 973} 974 975int 976do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 977 int pflag) 978{ 979 int local_fd, status; 980 u_int handle_len, id, type; 981 u_int64_t offset; 982 char *handle, *data; 983 Buffer msg; 984 struct stat sb; 985 Attrib a; 986 u_int32_t startid; 987 u_int32_t ackid; 988 struct outstanding_ack { 989 u_int id; 990 u_int len; 991 u_int64_t offset; 992 TAILQ_ENTRY(outstanding_ack) tq; 993 }; 994 TAILQ_HEAD(ackhead, outstanding_ack) acks; 995 struct outstanding_ack *ack = NULL; 996 997 TAILQ_INIT(&acks); 998 999 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { 1000 error("Couldn't open local file \"%s\" for reading: %s", 1001 local_path, strerror(errno)); 1002 return(-1); 1003 } 1004 if (fstat(local_fd, &sb) == -1) { 1005 error("Couldn't fstat local file \"%s\": %s", 1006 local_path, strerror(errno)); 1007 close(local_fd); 1008 return(-1); 1009 } 1010 if (!S_ISREG(sb.st_mode)) { 1011 error("%s is not a regular file", local_path); 1012 close(local_fd); 1013 return(-1); 1014 } 1015 stat_to_attrib(&sb, &a); 1016 1017 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 1018 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 1019 a.perm &= 0777; 1020 if (!pflag) 1021 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1022 1023 buffer_init(&msg); 1024 1025 /* Send open request */ 1026 id = conn->msg_id++; 1027 buffer_put_char(&msg, SSH2_FXP_OPEN); 1028 buffer_put_int(&msg, id); 1029 buffer_put_cstring(&msg, remote_path); 1030 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); 1031 encode_attrib(&msg, &a); 1032 send_msg(conn->fd_out, &msg); 1033 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 1034 1035 buffer_clear(&msg); 1036 1037 handle = get_handle(conn->fd_in, id, &handle_len); 1038 if (handle == NULL) { 1039 close(local_fd); 1040 buffer_free(&msg); 1041 return(-1); 1042 } 1043 1044 startid = ackid = id + 1; 1045 data = xmalloc(conn->transfer_buflen); 1046 1047 /* Read from local and write to remote */ 1048 offset = 0; 1049 if (showprogress) 1050 start_progress_meter(local_path, sb.st_size, &offset); 1051 1052 for (;;) { 1053 int len; 1054 1055 /* 1056 * Can't use atomicio here because it returns 0 on EOF, 1057 * thus losing the last block of the file. 1058 * Simulate an EOF on interrupt, allowing ACKs from the 1059 * server to drain. 1060 */ 1061 if (interrupted) 1062 len = 0; 1063 else do 1064 len = read(local_fd, data, conn->transfer_buflen); 1065 while ((len == -1) && (errno == EINTR || errno == EAGAIN)); 1066 1067 if (len == -1) 1068 fatal("Couldn't read from \"%s\": %s", local_path, 1069 strerror(errno)); 1070 1071 if (len != 0) { 1072 ack = xmalloc(sizeof(*ack)); 1073 ack->id = ++id; 1074 ack->offset = offset; 1075 ack->len = len; 1076 TAILQ_INSERT_TAIL(&acks, ack, tq); 1077 1078 buffer_clear(&msg); 1079 buffer_put_char(&msg, SSH2_FXP_WRITE); 1080 buffer_put_int(&msg, ack->id); 1081 buffer_put_string(&msg, handle, handle_len); 1082 buffer_put_int64(&msg, offset); 1083 buffer_put_string(&msg, data, len); 1084 send_msg(conn->fd_out, &msg); 1085 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", 1086 id, (unsigned long long)offset, len); 1087 } else if (TAILQ_FIRST(&acks) == NULL) 1088 break; 1089 1090 if (ack == NULL) 1091 fatal("Unexpected ACK %u", id); 1092 1093 if (id == startid || len == 0 || 1094 id - ackid >= conn->num_requests) { 1095 u_int r_id; 1096 1097 buffer_clear(&msg); 1098 get_msg(conn->fd_in, &msg); 1099 type = buffer_get_char(&msg); 1100 r_id = buffer_get_int(&msg); 1101 1102 if (type != SSH2_FXP_STATUS) 1103 fatal("Expected SSH2_FXP_STATUS(%d) packet, " 1104 "got %d", SSH2_FXP_STATUS, type); 1105 1106 status = buffer_get_int(&msg); 1107 debug3("SSH2_FXP_STATUS %d", status); 1108 1109 /* Find the request in our queue */ 1110 for(ack = TAILQ_FIRST(&acks); 1111 ack != NULL && ack->id != r_id; 1112 ack = TAILQ_NEXT(ack, tq)) 1113 ; 1114 if (ack == NULL) 1115 fatal("Can't find request for ID %u", r_id); 1116 TAILQ_REMOVE(&acks, ack, tq); 1117 1118 if (status != SSH2_FX_OK) { 1119 error("Couldn't write to remote file \"%s\": %s", 1120 remote_path, fx2txt(status)); 1121 do_close(conn, handle, handle_len); 1122 close(local_fd); 1123 xfree(data); 1124 xfree(ack); 1125 goto done; 1126 } 1127 debug3("In write loop, ack for %u %u bytes at %llu", 1128 ack->id, ack->len, (unsigned long long)ack->offset); 1129 ++ackid; 1130 xfree(ack); 1131 } 1132 offset += len; 1133 } 1134 if (showprogress) 1135 stop_progress_meter(); 1136 xfree(data); 1137 1138 if (close(local_fd) == -1) { 1139 error("Couldn't close local file \"%s\": %s", local_path, 1140 strerror(errno)); 1141 do_close(conn, handle, handle_len); 1142 status = -1; 1143 goto done; 1144 } 1145 1146 /* Override umask and utimes if asked */ 1147 if (pflag) 1148 do_fsetstat(conn, handle, handle_len, &a); 1149 1150 status = do_close(conn, handle, handle_len); 1151 1152done: 1153 xfree(handle); 1154 buffer_free(&msg); 1155 return(status); 1156} 1157