1*ce74bacaSMatthew Dillon /* $OpenBSD: sftp-client.c,v 1.127 2017/08/11 04:41:08 djm Exp $ */ 218de8d7fSPeter Avalos /* 318de8d7fSPeter Avalos * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 418de8d7fSPeter Avalos * 518de8d7fSPeter Avalos * Permission to use, copy, modify, and distribute this software for any 618de8d7fSPeter Avalos * purpose with or without fee is hereby granted, provided that the above 718de8d7fSPeter Avalos * copyright notice and this permission notice appear in all copies. 818de8d7fSPeter Avalos * 918de8d7fSPeter Avalos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1018de8d7fSPeter Avalos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1118de8d7fSPeter Avalos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1218de8d7fSPeter Avalos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1318de8d7fSPeter Avalos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1418de8d7fSPeter Avalos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1518de8d7fSPeter Avalos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1618de8d7fSPeter Avalos */ 1718de8d7fSPeter Avalos 1818de8d7fSPeter Avalos /* XXX: memleaks */ 1918de8d7fSPeter Avalos /* XXX: signed vs unsigned */ 2018de8d7fSPeter Avalos /* XXX: remove all logging, only return status codes */ 2118de8d7fSPeter Avalos /* XXX: copy between two remote sites */ 2218de8d7fSPeter Avalos 2318de8d7fSPeter Avalos #include "includes.h" 2418de8d7fSPeter Avalos 2518de8d7fSPeter Avalos #include <sys/types.h> 2618de8d7fSPeter Avalos #ifdef HAVE_SYS_STATVFS_H 2718de8d7fSPeter Avalos #include <sys/statvfs.h> 2818de8d7fSPeter Avalos #endif 2918de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h" 3018de8d7fSPeter Avalos #ifdef HAVE_SYS_STAT_H 3118de8d7fSPeter Avalos # include <sys/stat.h> 3218de8d7fSPeter Avalos #endif 3318de8d7fSPeter Avalos #ifdef HAVE_SYS_TIME_H 3418de8d7fSPeter Avalos # include <sys/time.h> 3518de8d7fSPeter Avalos #endif 3618de8d7fSPeter Avalos #include <sys/uio.h> 3718de8d7fSPeter Avalos 38856ea928SPeter Avalos #include <dirent.h> 3918de8d7fSPeter Avalos #include <errno.h> 4018de8d7fSPeter Avalos #include <fcntl.h> 4118de8d7fSPeter Avalos #include <signal.h> 4218de8d7fSPeter Avalos #include <stdarg.h> 4318de8d7fSPeter Avalos #include <stdio.h> 4436e94dc5SPeter Avalos #include <stdlib.h> 4518de8d7fSPeter Avalos #include <string.h> 4618de8d7fSPeter Avalos #include <unistd.h> 4718de8d7fSPeter Avalos 4818de8d7fSPeter Avalos #include "xmalloc.h" 49e9778795SPeter Avalos #include "ssherr.h" 50e9778795SPeter Avalos #include "sshbuf.h" 5118de8d7fSPeter Avalos #include "log.h" 5218de8d7fSPeter Avalos #include "atomicio.h" 5318de8d7fSPeter Avalos #include "progressmeter.h" 5418de8d7fSPeter Avalos #include "misc.h" 55e9778795SPeter Avalos #include "utf8.h" 5618de8d7fSPeter Avalos 5718de8d7fSPeter Avalos #include "sftp.h" 5818de8d7fSPeter Avalos #include "sftp-common.h" 5918de8d7fSPeter Avalos #include "sftp-client.h" 6018de8d7fSPeter Avalos 6118de8d7fSPeter Avalos extern volatile sig_atomic_t interrupted; 6218de8d7fSPeter Avalos extern int showprogress; 6318de8d7fSPeter Avalos 6418de8d7fSPeter Avalos /* Minimum amount of data to read at a time */ 6518de8d7fSPeter Avalos #define MIN_READ_SIZE 512 6618de8d7fSPeter Avalos 67856ea928SPeter Avalos /* Maximum depth to descend in directory trees */ 68856ea928SPeter Avalos #define MAX_DIR_DEPTH 64 69856ea928SPeter Avalos 70*ce74bacaSMatthew Dillon /* Directory separator characters */ 71*ce74bacaSMatthew Dillon #ifdef HAVE_CYGWIN 72*ce74bacaSMatthew Dillon # define SFTP_DIRECTORY_CHARS "/\\" 73*ce74bacaSMatthew Dillon #else /* HAVE_CYGWIN */ 74*ce74bacaSMatthew Dillon # define SFTP_DIRECTORY_CHARS "/" 75*ce74bacaSMatthew Dillon #endif /* HAVE_CYGWIN */ 76*ce74bacaSMatthew Dillon 7718de8d7fSPeter Avalos struct sftp_conn { 7818de8d7fSPeter Avalos int fd_in; 7918de8d7fSPeter Avalos int fd_out; 8018de8d7fSPeter Avalos u_int transfer_buflen; 8118de8d7fSPeter Avalos u_int num_requests; 8218de8d7fSPeter Avalos u_int version; 8318de8d7fSPeter Avalos u_int msg_id; 8418de8d7fSPeter Avalos #define SFTP_EXT_POSIX_RENAME 0x00000001 8518de8d7fSPeter Avalos #define SFTP_EXT_STATVFS 0x00000002 8618de8d7fSPeter Avalos #define SFTP_EXT_FSTATVFS 0x00000004 879f304aafSPeter Avalos #define SFTP_EXT_HARDLINK 0x00000008 8836e94dc5SPeter Avalos #define SFTP_EXT_FSYNC 0x00000010 8918de8d7fSPeter Avalos u_int exts; 909f304aafSPeter Avalos u_int64_t limit_kbps; 919f304aafSPeter Avalos struct bwlimit bwlimit_in, bwlimit_out; 9218de8d7fSPeter Avalos }; 9318de8d7fSPeter Avalos 94e9778795SPeter Avalos static u_char * 95e9778795SPeter Avalos get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, 969f304aafSPeter Avalos const char *errfmt, ...) __attribute__((format(printf, 4, 5))); 979f304aafSPeter Avalos 989f304aafSPeter Avalos /* ARGSUSED */ 999f304aafSPeter Avalos static int 1009f304aafSPeter Avalos sftpio(void *_bwlimit, size_t amount) 1019f304aafSPeter Avalos { 1029f304aafSPeter Avalos struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit; 1039f304aafSPeter Avalos 1049f304aafSPeter Avalos bandwidth_limit(bwlimit, amount); 1059f304aafSPeter Avalos return 0; 1069f304aafSPeter Avalos } 107856ea928SPeter Avalos 10818de8d7fSPeter Avalos static void 109e9778795SPeter Avalos send_msg(struct sftp_conn *conn, struct sshbuf *m) 11018de8d7fSPeter Avalos { 11118de8d7fSPeter Avalos u_char mlen[4]; 11218de8d7fSPeter Avalos struct iovec iov[2]; 11318de8d7fSPeter Avalos 114e9778795SPeter Avalos if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH) 115e9778795SPeter Avalos fatal("Outbound message too long %zu", sshbuf_len(m)); 11618de8d7fSPeter Avalos 11718de8d7fSPeter Avalos /* Send length first */ 118e9778795SPeter Avalos put_u32(mlen, sshbuf_len(m)); 11918de8d7fSPeter Avalos iov[0].iov_base = mlen; 12018de8d7fSPeter Avalos iov[0].iov_len = sizeof(mlen); 121e9778795SPeter Avalos iov[1].iov_base = (u_char *)sshbuf_ptr(m); 122e9778795SPeter Avalos iov[1].iov_len = sshbuf_len(m); 12318de8d7fSPeter Avalos 1249f304aafSPeter Avalos if (atomiciov6(writev, conn->fd_out, iov, 2, 1259f304aafSPeter Avalos conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) != 126e9778795SPeter Avalos sshbuf_len(m) + sizeof(mlen)) 12718de8d7fSPeter Avalos fatal("Couldn't send packet: %s", strerror(errno)); 12818de8d7fSPeter Avalos 129e9778795SPeter Avalos sshbuf_reset(m); 13018de8d7fSPeter Avalos } 13118de8d7fSPeter Avalos 13218de8d7fSPeter Avalos static void 133e9778795SPeter Avalos get_msg(struct sftp_conn *conn, struct sshbuf *m) 13418de8d7fSPeter Avalos { 13518de8d7fSPeter Avalos u_int msg_len; 136e9778795SPeter Avalos u_char *p; 137e9778795SPeter Avalos int r; 13818de8d7fSPeter Avalos 139e9778795SPeter Avalos if ((r = sshbuf_reserve(m, 4, &p)) != 0) 140e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 141e9778795SPeter Avalos if (atomicio6(read, conn->fd_in, p, 4, 1429f304aafSPeter Avalos conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) { 143*ce74bacaSMatthew Dillon if (errno == EPIPE || errno == ECONNRESET) 14418de8d7fSPeter Avalos fatal("Connection closed"); 14518de8d7fSPeter Avalos else 14618de8d7fSPeter Avalos fatal("Couldn't read packet: %s", strerror(errno)); 14718de8d7fSPeter Avalos } 14818de8d7fSPeter Avalos 149e9778795SPeter Avalos if ((r = sshbuf_get_u32(m, &msg_len)) != 0) 150e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 15118de8d7fSPeter Avalos if (msg_len > SFTP_MAX_MSG_LENGTH) 15218de8d7fSPeter Avalos fatal("Received message too long %u", msg_len); 15318de8d7fSPeter Avalos 154e9778795SPeter Avalos if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) 155e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 156e9778795SPeter Avalos if (atomicio6(read, conn->fd_in, p, msg_len, 1579f304aafSPeter Avalos conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) 1589f304aafSPeter Avalos != msg_len) { 15918de8d7fSPeter Avalos if (errno == EPIPE) 16018de8d7fSPeter Avalos fatal("Connection closed"); 16118de8d7fSPeter Avalos else 16218de8d7fSPeter Avalos fatal("Read packet: %s", strerror(errno)); 16318de8d7fSPeter Avalos } 16418de8d7fSPeter Avalos } 16518de8d7fSPeter Avalos 16618de8d7fSPeter Avalos static void 167e9778795SPeter Avalos send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s, 16818de8d7fSPeter Avalos u_int len) 16918de8d7fSPeter Avalos { 170e9778795SPeter Avalos struct sshbuf *msg; 171e9778795SPeter Avalos int r; 17218de8d7fSPeter Avalos 173e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 174e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 175e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, code)) != 0 || 176e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 177e9778795SPeter Avalos (r = sshbuf_put_string(msg, s, len)) != 0) 178e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 179e9778795SPeter Avalos send_msg(conn, msg); 1809f304aafSPeter Avalos debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); 181e9778795SPeter Avalos sshbuf_free(msg); 18218de8d7fSPeter Avalos } 18318de8d7fSPeter Avalos 18418de8d7fSPeter Avalos static void 1859f304aafSPeter Avalos send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code, 186e9778795SPeter Avalos const void *s, u_int len, Attrib *a) 18718de8d7fSPeter Avalos { 188e9778795SPeter Avalos struct sshbuf *msg; 189e9778795SPeter Avalos int r; 19018de8d7fSPeter Avalos 191e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 192e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 193e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, code)) != 0 || 194e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 195e9778795SPeter Avalos (r = sshbuf_put_string(msg, s, len)) != 0 || 196e9778795SPeter Avalos (r = encode_attrib(msg, a)) != 0) 197e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 198e9778795SPeter Avalos send_msg(conn, msg); 1999f304aafSPeter Avalos debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); 200e9778795SPeter Avalos sshbuf_free(msg); 20118de8d7fSPeter Avalos } 20218de8d7fSPeter Avalos 20318de8d7fSPeter Avalos static u_int 2049f304aafSPeter Avalos get_status(struct sftp_conn *conn, u_int expected_id) 20518de8d7fSPeter Avalos { 206e9778795SPeter Avalos struct sshbuf *msg; 207e9778795SPeter Avalos u_char type; 208e9778795SPeter Avalos u_int id, status; 209e9778795SPeter Avalos int r; 21018de8d7fSPeter Avalos 211e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 212e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 213e9778795SPeter Avalos get_msg(conn, msg); 214e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 215e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &id)) != 0) 216e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 21718de8d7fSPeter Avalos 21818de8d7fSPeter Avalos if (id != expected_id) 21918de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 22018de8d7fSPeter Avalos if (type != SSH2_FXP_STATUS) 22118de8d7fSPeter Avalos fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", 22218de8d7fSPeter Avalos SSH2_FXP_STATUS, type); 22318de8d7fSPeter Avalos 224e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &status)) != 0) 225e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 226e9778795SPeter Avalos sshbuf_free(msg); 22718de8d7fSPeter Avalos 22818de8d7fSPeter Avalos debug3("SSH2_FXP_STATUS %u", status); 22918de8d7fSPeter Avalos 2309f304aafSPeter Avalos return status; 23118de8d7fSPeter Avalos } 23218de8d7fSPeter Avalos 233e9778795SPeter Avalos static u_char * 234e9778795SPeter Avalos get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, 2359f304aafSPeter Avalos const char *errfmt, ...) 23618de8d7fSPeter Avalos { 237e9778795SPeter Avalos struct sshbuf *msg; 238e9778795SPeter Avalos u_int id, status; 239e9778795SPeter Avalos u_char type; 240e9778795SPeter Avalos u_char *handle; 241e9778795SPeter Avalos char errmsg[256]; 242856ea928SPeter Avalos va_list args; 243e9778795SPeter Avalos int r; 244856ea928SPeter Avalos 245856ea928SPeter Avalos va_start(args, errfmt); 246856ea928SPeter Avalos if (errfmt != NULL) 247856ea928SPeter Avalos vsnprintf(errmsg, sizeof(errmsg), errfmt, args); 248856ea928SPeter Avalos va_end(args); 24918de8d7fSPeter Avalos 250e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 251e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 252e9778795SPeter Avalos get_msg(conn, msg); 253e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 254e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &id)) != 0) 255e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 25618de8d7fSPeter Avalos 25718de8d7fSPeter Avalos if (id != expected_id) 258856ea928SPeter Avalos fatal("%s: ID mismatch (%u != %u)", 259856ea928SPeter Avalos errfmt == NULL ? __func__ : errmsg, id, expected_id); 26018de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 261e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &status)) != 0) 262e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 263856ea928SPeter Avalos if (errfmt != NULL) 264856ea928SPeter Avalos error("%s: %s", errmsg, fx2txt(status)); 265e9778795SPeter Avalos sshbuf_free(msg); 26618de8d7fSPeter Avalos return(NULL); 26718de8d7fSPeter Avalos } else if (type != SSH2_FXP_HANDLE) 268856ea928SPeter Avalos fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", 269856ea928SPeter Avalos errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); 27018de8d7fSPeter Avalos 271e9778795SPeter Avalos if ((r = sshbuf_get_string(msg, &handle, len)) != 0) 272e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 273e9778795SPeter Avalos sshbuf_free(msg); 27418de8d7fSPeter Avalos 275e9778795SPeter Avalos return handle; 27618de8d7fSPeter Avalos } 27718de8d7fSPeter Avalos 27818de8d7fSPeter Avalos static Attrib * 2799f304aafSPeter Avalos get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet) 28018de8d7fSPeter Avalos { 281e9778795SPeter Avalos struct sshbuf *msg; 282e9778795SPeter Avalos u_int id; 283e9778795SPeter Avalos u_char type; 284e9778795SPeter Avalos int r; 285e9778795SPeter Avalos static Attrib a; 28618de8d7fSPeter Avalos 287e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 288e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 289e9778795SPeter Avalos get_msg(conn, msg); 29018de8d7fSPeter Avalos 291e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 292e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &id)) != 0) 293e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 29418de8d7fSPeter Avalos 29518de8d7fSPeter Avalos debug3("Received stat reply T:%u I:%u", type, id); 29618de8d7fSPeter Avalos if (id != expected_id) 29718de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 29818de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 299e9778795SPeter Avalos u_int status; 30018de8d7fSPeter Avalos 301e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &status)) != 0) 302e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 30318de8d7fSPeter Avalos if (quiet) 30418de8d7fSPeter Avalos debug("Couldn't stat remote file: %s", fx2txt(status)); 30518de8d7fSPeter Avalos else 30618de8d7fSPeter Avalos error("Couldn't stat remote file: %s", fx2txt(status)); 307e9778795SPeter Avalos sshbuf_free(msg); 30818de8d7fSPeter Avalos return(NULL); 30918de8d7fSPeter Avalos } else if (type != SSH2_FXP_ATTRS) { 31018de8d7fSPeter Avalos fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", 31118de8d7fSPeter Avalos SSH2_FXP_ATTRS, type); 31218de8d7fSPeter Avalos } 313e9778795SPeter Avalos if ((r = decode_attrib(msg, &a)) != 0) { 314e9778795SPeter Avalos error("%s: couldn't decode attrib: %s", __func__, ssh_err(r)); 315e9778795SPeter Avalos sshbuf_free(msg); 316e9778795SPeter Avalos return NULL; 317e9778795SPeter Avalos } 318e9778795SPeter Avalos sshbuf_free(msg); 31918de8d7fSPeter Avalos 320e9778795SPeter Avalos return &a; 32118de8d7fSPeter Avalos } 32218de8d7fSPeter Avalos 32318de8d7fSPeter Avalos static int 3249f304aafSPeter Avalos get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st, 3259f304aafSPeter Avalos u_int expected_id, int quiet) 32618de8d7fSPeter Avalos { 327e9778795SPeter Avalos struct sshbuf *msg; 328e9778795SPeter Avalos u_char type; 329e9778795SPeter Avalos u_int id; 330e9778795SPeter Avalos u_int64_t flag; 331e9778795SPeter Avalos int r; 33218de8d7fSPeter Avalos 333e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 334e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 335e9778795SPeter Avalos get_msg(conn, msg); 33618de8d7fSPeter Avalos 337e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 338e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &id)) != 0) 339e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 34018de8d7fSPeter Avalos 34118de8d7fSPeter Avalos debug3("Received statvfs reply T:%u I:%u", type, id); 34218de8d7fSPeter Avalos if (id != expected_id) 34318de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 34418de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 345e9778795SPeter Avalos u_int status; 34618de8d7fSPeter Avalos 347e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &status)) != 0) 348e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 34918de8d7fSPeter Avalos if (quiet) 35018de8d7fSPeter Avalos debug("Couldn't statvfs: %s", fx2txt(status)); 35118de8d7fSPeter Avalos else 35218de8d7fSPeter Avalos error("Couldn't statvfs: %s", fx2txt(status)); 353e9778795SPeter Avalos sshbuf_free(msg); 35418de8d7fSPeter Avalos return -1; 35518de8d7fSPeter Avalos } else if (type != SSH2_FXP_EXTENDED_REPLY) { 35618de8d7fSPeter Avalos fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", 35718de8d7fSPeter Avalos SSH2_FXP_EXTENDED_REPLY, type); 35818de8d7fSPeter Avalos } 35918de8d7fSPeter Avalos 36036e94dc5SPeter Avalos memset(st, 0, sizeof(*st)); 361e9778795SPeter Avalos if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 || 362e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 || 363e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 || 364e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 || 365e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 || 366e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_files)) != 0 || 367e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 || 368e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 || 369e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 || 370e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &flag)) != 0 || 371e9778795SPeter Avalos (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0) 372e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 37318de8d7fSPeter Avalos 37418de8d7fSPeter Avalos st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0; 37518de8d7fSPeter Avalos st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0; 37618de8d7fSPeter Avalos 377e9778795SPeter Avalos sshbuf_free(msg); 37818de8d7fSPeter Avalos 37918de8d7fSPeter Avalos return 0; 38018de8d7fSPeter Avalos } 38118de8d7fSPeter Avalos 38218de8d7fSPeter Avalos struct sftp_conn * 3839f304aafSPeter Avalos do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, 3849f304aafSPeter Avalos u_int64_t limit_kbps) 38518de8d7fSPeter Avalos { 386e9778795SPeter Avalos u_char type; 387e9778795SPeter Avalos struct sshbuf *msg; 38818de8d7fSPeter Avalos struct sftp_conn *ret; 389e9778795SPeter Avalos int r; 39018de8d7fSPeter Avalos 39136e94dc5SPeter Avalos ret = xcalloc(1, sizeof(*ret)); 39236e94dc5SPeter Avalos ret->msg_id = 1; 3939f304aafSPeter Avalos ret->fd_in = fd_in; 3949f304aafSPeter Avalos ret->fd_out = fd_out; 3959f304aafSPeter Avalos ret->transfer_buflen = transfer_buflen; 3969f304aafSPeter Avalos ret->num_requests = num_requests; 3979f304aafSPeter Avalos ret->exts = 0; 3989f304aafSPeter Avalos ret->limit_kbps = 0; 3999f304aafSPeter Avalos 400e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 401e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 402e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 || 403e9778795SPeter Avalos (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) 404e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 405e9778795SPeter Avalos send_msg(ret, msg); 40618de8d7fSPeter Avalos 407e9778795SPeter Avalos sshbuf_reset(msg); 40818de8d7fSPeter Avalos 409e9778795SPeter Avalos get_msg(ret, msg); 41018de8d7fSPeter Avalos 41118de8d7fSPeter Avalos /* Expecting a VERSION reply */ 412e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0) 413e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 414e9778795SPeter Avalos if (type != SSH2_FXP_VERSION) { 41518de8d7fSPeter Avalos error("Invalid packet back from SSH2_FXP_INIT (type %u)", 41618de8d7fSPeter Avalos type); 417e9778795SPeter Avalos sshbuf_free(msg); 418e9778795SPeter Avalos free(ret); 41918de8d7fSPeter Avalos return(NULL); 42018de8d7fSPeter Avalos } 421e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &ret->version)) != 0) 422e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 42318de8d7fSPeter Avalos 4249f304aafSPeter Avalos debug2("Remote version: %u", ret->version); 42518de8d7fSPeter Avalos 42618de8d7fSPeter Avalos /* Check for extensions */ 427e9778795SPeter Avalos while (sshbuf_len(msg) > 0) { 428e9778795SPeter Avalos char *name; 429e9778795SPeter Avalos u_char *value; 430e9778795SPeter Avalos size_t vlen; 43118de8d7fSPeter Avalos int known = 0; 43218de8d7fSPeter Avalos 433e9778795SPeter Avalos if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 || 434e9778795SPeter Avalos (r = sshbuf_get_string(msg, &value, &vlen)) != 0) 435e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 43618de8d7fSPeter Avalos if (strcmp(name, "posix-rename@openssh.com") == 0 && 437e9778795SPeter Avalos strcmp((char *)value, "1") == 0) { 4389f304aafSPeter Avalos ret->exts |= SFTP_EXT_POSIX_RENAME; 43918de8d7fSPeter Avalos known = 1; 44018de8d7fSPeter Avalos } else if (strcmp(name, "statvfs@openssh.com") == 0 && 441e9778795SPeter Avalos strcmp((char *)value, "2") == 0) { 4429f304aafSPeter Avalos ret->exts |= SFTP_EXT_STATVFS; 44318de8d7fSPeter Avalos known = 1; 4449f304aafSPeter Avalos } else if (strcmp(name, "fstatvfs@openssh.com") == 0 && 445e9778795SPeter Avalos strcmp((char *)value, "2") == 0) { 4469f304aafSPeter Avalos ret->exts |= SFTP_EXT_FSTATVFS; 4479f304aafSPeter Avalos known = 1; 4489f304aafSPeter Avalos } else if (strcmp(name, "hardlink@openssh.com") == 0 && 449e9778795SPeter Avalos strcmp((char *)value, "1") == 0) { 4509f304aafSPeter Avalos ret->exts |= SFTP_EXT_HARDLINK; 45118de8d7fSPeter Avalos known = 1; 45236e94dc5SPeter Avalos } else if (strcmp(name, "fsync@openssh.com") == 0 && 453e9778795SPeter Avalos strcmp((char *)value, "1") == 0) { 45436e94dc5SPeter Avalos ret->exts |= SFTP_EXT_FSYNC; 45536e94dc5SPeter Avalos known = 1; 45618de8d7fSPeter Avalos } 45718de8d7fSPeter Avalos if (known) { 45818de8d7fSPeter Avalos debug2("Server supports extension \"%s\" revision %s", 45918de8d7fSPeter Avalos name, value); 46018de8d7fSPeter Avalos } else { 46118de8d7fSPeter Avalos debug2("Unrecognised server extension \"%s\"", name); 46218de8d7fSPeter Avalos } 46336e94dc5SPeter Avalos free(name); 46436e94dc5SPeter Avalos free(value); 46518de8d7fSPeter Avalos } 46618de8d7fSPeter Avalos 467e9778795SPeter Avalos sshbuf_free(msg); 46818de8d7fSPeter Avalos 46918de8d7fSPeter Avalos /* Some filexfer v.0 servers don't support large packets */ 4709f304aafSPeter Avalos if (ret->version == 0) 471*ce74bacaSMatthew Dillon ret->transfer_buflen = MINIMUM(ret->transfer_buflen, 20480); 47218de8d7fSPeter Avalos 4739f304aafSPeter Avalos ret->limit_kbps = limit_kbps; 4749f304aafSPeter Avalos if (ret->limit_kbps > 0) { 4759f304aafSPeter Avalos bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps, 4769f304aafSPeter Avalos ret->transfer_buflen); 4779f304aafSPeter Avalos bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps, 4789f304aafSPeter Avalos ret->transfer_buflen); 4799f304aafSPeter Avalos } 4809f304aafSPeter Avalos 4819f304aafSPeter Avalos return ret; 48218de8d7fSPeter Avalos } 48318de8d7fSPeter Avalos 48418de8d7fSPeter Avalos u_int 48518de8d7fSPeter Avalos sftp_proto_version(struct sftp_conn *conn) 48618de8d7fSPeter Avalos { 4879f304aafSPeter Avalos return conn->version; 48818de8d7fSPeter Avalos } 48918de8d7fSPeter Avalos 49018de8d7fSPeter Avalos int 491e9778795SPeter Avalos do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len) 49218de8d7fSPeter Avalos { 49318de8d7fSPeter Avalos u_int id, status; 494e9778795SPeter Avalos struct sshbuf *msg; 495e9778795SPeter Avalos int r; 49618de8d7fSPeter Avalos 497e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 498e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 49918de8d7fSPeter Avalos 50018de8d7fSPeter Avalos id = conn->msg_id++; 501e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 || 502e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 503e9778795SPeter Avalos (r = sshbuf_put_string(msg, handle, handle_len)) != 0) 504e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 505e9778795SPeter Avalos send_msg(conn, msg); 50618de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_CLOSE I:%u", id); 50718de8d7fSPeter Avalos 5089f304aafSPeter Avalos status = get_status(conn, id); 50918de8d7fSPeter Avalos if (status != SSH2_FX_OK) 51018de8d7fSPeter Avalos error("Couldn't close file: %s", fx2txt(status)); 51118de8d7fSPeter Avalos 512e9778795SPeter Avalos sshbuf_free(msg); 51318de8d7fSPeter Avalos 514e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 51518de8d7fSPeter Avalos } 51618de8d7fSPeter Avalos 51718de8d7fSPeter Avalos 51818de8d7fSPeter Avalos static int 519e9778795SPeter Avalos do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag, 52018de8d7fSPeter Avalos SFTP_DIRENT ***dir) 52118de8d7fSPeter Avalos { 522e9778795SPeter Avalos struct sshbuf *msg; 523e9778795SPeter Avalos u_int count, id, i, expected_id, ents = 0; 524e9778795SPeter Avalos size_t handle_len; 525e9778795SPeter Avalos u_char type, *handle; 52636e94dc5SPeter Avalos int status = SSH2_FX_FAILURE; 527e9778795SPeter Avalos int r; 52836e94dc5SPeter Avalos 52936e94dc5SPeter Avalos if (dir) 53036e94dc5SPeter Avalos *dir = NULL; 53118de8d7fSPeter Avalos 53218de8d7fSPeter Avalos id = conn->msg_id++; 53318de8d7fSPeter Avalos 534e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 535e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 536e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 || 537e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 538e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, path)) != 0) 539e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 540e9778795SPeter Avalos send_msg(conn, msg); 54118de8d7fSPeter Avalos 5429f304aafSPeter Avalos handle = get_handle(conn, id, &handle_len, 543856ea928SPeter Avalos "remote readdir(\"%s\")", path); 54499e85e0dSPeter Avalos if (handle == NULL) { 545e9778795SPeter Avalos sshbuf_free(msg); 5469f304aafSPeter Avalos return -1; 54799e85e0dSPeter Avalos } 54818de8d7fSPeter Avalos 54918de8d7fSPeter Avalos if (dir) { 55018de8d7fSPeter Avalos ents = 0; 55136e94dc5SPeter Avalos *dir = xcalloc(1, sizeof(**dir)); 55218de8d7fSPeter Avalos (*dir)[0] = NULL; 55318de8d7fSPeter Avalos } 55418de8d7fSPeter Avalos 55518de8d7fSPeter Avalos for (; !interrupted;) { 55618de8d7fSPeter Avalos id = expected_id = conn->msg_id++; 55718de8d7fSPeter Avalos 55818de8d7fSPeter Avalos debug3("Sending SSH2_FXP_READDIR I:%u", id); 55918de8d7fSPeter Avalos 560e9778795SPeter Avalos sshbuf_reset(msg); 561e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 || 562e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 563e9778795SPeter Avalos (r = sshbuf_put_string(msg, handle, handle_len)) != 0) 564e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 565e9778795SPeter Avalos send_msg(conn, msg); 56618de8d7fSPeter Avalos 567e9778795SPeter Avalos sshbuf_reset(msg); 56818de8d7fSPeter Avalos 569e9778795SPeter Avalos get_msg(conn, msg); 57018de8d7fSPeter Avalos 571e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 572e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &id)) != 0) 573e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 57418de8d7fSPeter Avalos 57518de8d7fSPeter Avalos debug3("Received reply T:%u I:%u", type, id); 57618de8d7fSPeter Avalos 57718de8d7fSPeter Avalos if (id != expected_id) 57818de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 57918de8d7fSPeter Avalos 58018de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 581e9778795SPeter Avalos u_int rstatus; 582e9778795SPeter Avalos 583e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &rstatus)) != 0) 584e9778795SPeter Avalos fatal("%s: buffer error: %s", 585e9778795SPeter Avalos __func__, ssh_err(r)); 586e9778795SPeter Avalos debug3("Received SSH2_FXP_STATUS %d", rstatus); 587e9778795SPeter Avalos if (rstatus == SSH2_FX_EOF) 58818de8d7fSPeter Avalos break; 589e9778795SPeter Avalos error("Couldn't read directory: %s", fx2txt(rstatus)); 59036e94dc5SPeter Avalos goto out; 59118de8d7fSPeter Avalos } else if (type != SSH2_FXP_NAME) 59218de8d7fSPeter Avalos fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 59318de8d7fSPeter Avalos SSH2_FXP_NAME, type); 59418de8d7fSPeter Avalos 595e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &count)) != 0) 596e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 597*ce74bacaSMatthew Dillon if (count > SSHBUF_SIZE_MAX) 598*ce74bacaSMatthew Dillon fatal("%s: nonsensical number of entries", __func__); 59918de8d7fSPeter Avalos if (count == 0) 60018de8d7fSPeter Avalos break; 60118de8d7fSPeter Avalos debug3("Received %d SSH2_FXP_NAME responses", count); 60218de8d7fSPeter Avalos for (i = 0; i < count; i++) { 60318de8d7fSPeter Avalos char *filename, *longname; 604e9778795SPeter Avalos Attrib a; 60518de8d7fSPeter Avalos 606e9778795SPeter Avalos if ((r = sshbuf_get_cstring(msg, &filename, 607e9778795SPeter Avalos NULL)) != 0 || 608e9778795SPeter Avalos (r = sshbuf_get_cstring(msg, &longname, 609e9778795SPeter Avalos NULL)) != 0) 610e9778795SPeter Avalos fatal("%s: buffer error: %s", 611e9778795SPeter Avalos __func__, ssh_err(r)); 612e9778795SPeter Avalos if ((r = decode_attrib(msg, &a)) != 0) { 613e9778795SPeter Avalos error("%s: couldn't decode attrib: %s", 614e9778795SPeter Avalos __func__, ssh_err(r)); 615e9778795SPeter Avalos free(filename); 616e9778795SPeter Avalos free(longname); 617e9778795SPeter Avalos sshbuf_free(msg); 618e9778795SPeter Avalos return -1; 619e9778795SPeter Avalos } 62018de8d7fSPeter Avalos 62136e94dc5SPeter Avalos if (print_flag) 622e9778795SPeter Avalos mprintf("%s\n", longname); 62318de8d7fSPeter Avalos 624856ea928SPeter Avalos /* 625856ea928SPeter Avalos * Directory entries should never contain '/' 626856ea928SPeter Avalos * These can be used to attack recursive ops 627856ea928SPeter Avalos * (e.g. send '../../../../etc/passwd') 628856ea928SPeter Avalos */ 629*ce74bacaSMatthew Dillon if (strpbrk(filename, SFTP_DIRECTORY_CHARS) != NULL) { 630856ea928SPeter Avalos error("Server sent suspect path \"%s\" " 631856ea928SPeter Avalos "during readdir of \"%s\"", filename, path); 63236e94dc5SPeter Avalos } else if (dir) { 633e9778795SPeter Avalos *dir = xreallocarray(*dir, ents + 2, sizeof(**dir)); 63436e94dc5SPeter Avalos (*dir)[ents] = xcalloc(1, sizeof(***dir)); 63518de8d7fSPeter Avalos (*dir)[ents]->filename = xstrdup(filename); 63618de8d7fSPeter Avalos (*dir)[ents]->longname = xstrdup(longname); 637e9778795SPeter Avalos memcpy(&(*dir)[ents]->a, &a, sizeof(a)); 63818de8d7fSPeter Avalos (*dir)[++ents] = NULL; 63918de8d7fSPeter Avalos } 64036e94dc5SPeter Avalos free(filename); 64136e94dc5SPeter Avalos free(longname); 64218de8d7fSPeter Avalos } 64318de8d7fSPeter Avalos } 64436e94dc5SPeter Avalos status = 0; 64518de8d7fSPeter Avalos 64636e94dc5SPeter Avalos out: 647e9778795SPeter Avalos sshbuf_free(msg); 64818de8d7fSPeter Avalos do_close(conn, handle, handle_len); 64936e94dc5SPeter Avalos free(handle); 65018de8d7fSPeter Avalos 65136e94dc5SPeter Avalos if (status != 0 && dir != NULL) { 65236e94dc5SPeter Avalos /* Don't return results on error */ 65318de8d7fSPeter Avalos free_sftp_dirents(*dir); 65436e94dc5SPeter Avalos *dir = NULL; 65536e94dc5SPeter Avalos } else if (interrupted && dir != NULL && *dir != NULL) { 65636e94dc5SPeter Avalos /* Don't return partial matches on interrupt */ 65736e94dc5SPeter Avalos free_sftp_dirents(*dir); 65836e94dc5SPeter Avalos *dir = xcalloc(1, sizeof(**dir)); 65918de8d7fSPeter Avalos **dir = NULL; 66018de8d7fSPeter Avalos } 66118de8d7fSPeter Avalos 66236e94dc5SPeter Avalos return status; 66318de8d7fSPeter Avalos } 66418de8d7fSPeter Avalos 66518de8d7fSPeter Avalos int 666e9778795SPeter Avalos do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir) 66718de8d7fSPeter Avalos { 66818de8d7fSPeter Avalos return(do_lsreaddir(conn, path, 0, dir)); 66918de8d7fSPeter Avalos } 67018de8d7fSPeter Avalos 67118de8d7fSPeter Avalos void free_sftp_dirents(SFTP_DIRENT **s) 67218de8d7fSPeter Avalos { 67318de8d7fSPeter Avalos int i; 67418de8d7fSPeter Avalos 67536e94dc5SPeter Avalos if (s == NULL) 67636e94dc5SPeter Avalos return; 67718de8d7fSPeter Avalos for (i = 0; s[i]; i++) { 67836e94dc5SPeter Avalos free(s[i]->filename); 67936e94dc5SPeter Avalos free(s[i]->longname); 68036e94dc5SPeter Avalos free(s[i]); 68118de8d7fSPeter Avalos } 68236e94dc5SPeter Avalos free(s); 68318de8d7fSPeter Avalos } 68418de8d7fSPeter Avalos 68518de8d7fSPeter Avalos int 686e9778795SPeter Avalos do_rm(struct sftp_conn *conn, const char *path) 68718de8d7fSPeter Avalos { 68818de8d7fSPeter Avalos u_int status, id; 68918de8d7fSPeter Avalos 69018de8d7fSPeter Avalos debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); 69118de8d7fSPeter Avalos 69218de8d7fSPeter Avalos id = conn->msg_id++; 6939f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path)); 6949f304aafSPeter Avalos status = get_status(conn, id); 69518de8d7fSPeter Avalos if (status != SSH2_FX_OK) 69618de8d7fSPeter Avalos error("Couldn't delete file: %s", fx2txt(status)); 697e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 69818de8d7fSPeter Avalos } 69918de8d7fSPeter Avalos 70018de8d7fSPeter Avalos int 701e9778795SPeter Avalos do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag) 70218de8d7fSPeter Avalos { 70318de8d7fSPeter Avalos u_int status, id; 70418de8d7fSPeter Avalos 70518de8d7fSPeter Avalos id = conn->msg_id++; 7069f304aafSPeter Avalos send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path, 70718de8d7fSPeter Avalos strlen(path), a); 70818de8d7fSPeter Avalos 7099f304aafSPeter Avalos status = get_status(conn, id); 71036e94dc5SPeter Avalos if (status != SSH2_FX_OK && print_flag) 71118de8d7fSPeter Avalos error("Couldn't create directory: %s", fx2txt(status)); 71218de8d7fSPeter Avalos 713e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 71418de8d7fSPeter Avalos } 71518de8d7fSPeter Avalos 71618de8d7fSPeter Avalos int 717e9778795SPeter Avalos do_rmdir(struct sftp_conn *conn, const char *path) 71818de8d7fSPeter Avalos { 71918de8d7fSPeter Avalos u_int status, id; 72018de8d7fSPeter Avalos 72118de8d7fSPeter Avalos id = conn->msg_id++; 7229f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_RMDIR, path, 72318de8d7fSPeter Avalos strlen(path)); 72418de8d7fSPeter Avalos 7259f304aafSPeter Avalos status = get_status(conn, id); 72618de8d7fSPeter Avalos if (status != SSH2_FX_OK) 72718de8d7fSPeter Avalos error("Couldn't remove directory: %s", fx2txt(status)); 72818de8d7fSPeter Avalos 729e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 73018de8d7fSPeter Avalos } 73118de8d7fSPeter Avalos 73218de8d7fSPeter Avalos Attrib * 733e9778795SPeter Avalos do_stat(struct sftp_conn *conn, const char *path, int quiet) 73418de8d7fSPeter Avalos { 73518de8d7fSPeter Avalos u_int id; 73618de8d7fSPeter Avalos 73718de8d7fSPeter Avalos id = conn->msg_id++; 73818de8d7fSPeter Avalos 7399f304aafSPeter Avalos send_string_request(conn, id, 74018de8d7fSPeter Avalos conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, 74118de8d7fSPeter Avalos path, strlen(path)); 74218de8d7fSPeter Avalos 7439f304aafSPeter Avalos return(get_decode_stat(conn, id, quiet)); 74418de8d7fSPeter Avalos } 74518de8d7fSPeter Avalos 74618de8d7fSPeter Avalos Attrib * 747e9778795SPeter Avalos do_lstat(struct sftp_conn *conn, const char *path, int quiet) 74818de8d7fSPeter Avalos { 74918de8d7fSPeter Avalos u_int id; 75018de8d7fSPeter Avalos 75118de8d7fSPeter Avalos if (conn->version == 0) { 75218de8d7fSPeter Avalos if (quiet) 75318de8d7fSPeter Avalos debug("Server version does not support lstat operation"); 75418de8d7fSPeter Avalos else 75518de8d7fSPeter Avalos logit("Server version does not support lstat operation"); 75618de8d7fSPeter Avalos return(do_stat(conn, path, quiet)); 75718de8d7fSPeter Avalos } 75818de8d7fSPeter Avalos 75918de8d7fSPeter Avalos id = conn->msg_id++; 7609f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_LSTAT, path, 76118de8d7fSPeter Avalos strlen(path)); 76218de8d7fSPeter Avalos 7639f304aafSPeter Avalos return(get_decode_stat(conn, id, quiet)); 76418de8d7fSPeter Avalos } 76518de8d7fSPeter Avalos 76618de8d7fSPeter Avalos #ifdef notyet 76718de8d7fSPeter Avalos Attrib * 768e9778795SPeter Avalos do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, 769e9778795SPeter Avalos int quiet) 77018de8d7fSPeter Avalos { 77118de8d7fSPeter Avalos u_int id; 77218de8d7fSPeter Avalos 77318de8d7fSPeter Avalos id = conn->msg_id++; 7749f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_FSTAT, handle, 77518de8d7fSPeter Avalos handle_len); 77618de8d7fSPeter Avalos 7779f304aafSPeter Avalos return(get_decode_stat(conn, id, quiet)); 77818de8d7fSPeter Avalos } 77918de8d7fSPeter Avalos #endif 78018de8d7fSPeter Avalos 78118de8d7fSPeter Avalos int 782e9778795SPeter Avalos do_setstat(struct sftp_conn *conn, const char *path, Attrib *a) 78318de8d7fSPeter Avalos { 78418de8d7fSPeter Avalos u_int status, id; 78518de8d7fSPeter Avalos 78618de8d7fSPeter Avalos id = conn->msg_id++; 7879f304aafSPeter Avalos send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path, 78818de8d7fSPeter Avalos strlen(path), a); 78918de8d7fSPeter Avalos 7909f304aafSPeter Avalos status = get_status(conn, id); 79118de8d7fSPeter Avalos if (status != SSH2_FX_OK) 79218de8d7fSPeter Avalos error("Couldn't setstat on \"%s\": %s", path, 79318de8d7fSPeter Avalos fx2txt(status)); 79418de8d7fSPeter Avalos 795e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 79618de8d7fSPeter Avalos } 79718de8d7fSPeter Avalos 79818de8d7fSPeter Avalos int 799e9778795SPeter Avalos do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, 80018de8d7fSPeter Avalos Attrib *a) 80118de8d7fSPeter Avalos { 80218de8d7fSPeter Avalos u_int status, id; 80318de8d7fSPeter Avalos 80418de8d7fSPeter Avalos id = conn->msg_id++; 8059f304aafSPeter Avalos send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle, 80618de8d7fSPeter Avalos handle_len, a); 80718de8d7fSPeter Avalos 8089f304aafSPeter Avalos status = get_status(conn, id); 80918de8d7fSPeter Avalos if (status != SSH2_FX_OK) 81018de8d7fSPeter Avalos error("Couldn't fsetstat: %s", fx2txt(status)); 81118de8d7fSPeter Avalos 812e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 81318de8d7fSPeter Avalos } 81418de8d7fSPeter Avalos 81518de8d7fSPeter Avalos char * 816e9778795SPeter Avalos do_realpath(struct sftp_conn *conn, const char *path) 81718de8d7fSPeter Avalos { 818e9778795SPeter Avalos struct sshbuf *msg; 819e9778795SPeter Avalos u_int expected_id, count, id; 82018de8d7fSPeter Avalos char *filename, *longname; 821e9778795SPeter Avalos Attrib a; 822e9778795SPeter Avalos u_char type; 823e9778795SPeter Avalos int r; 82418de8d7fSPeter Avalos 82518de8d7fSPeter Avalos expected_id = id = conn->msg_id++; 8269f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_REALPATH, path, 82718de8d7fSPeter Avalos strlen(path)); 82818de8d7fSPeter Avalos 829e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 830e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 83118de8d7fSPeter Avalos 832e9778795SPeter Avalos get_msg(conn, msg); 833e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 834e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &id)) != 0) 835e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 83618de8d7fSPeter Avalos 83718de8d7fSPeter Avalos if (id != expected_id) 83818de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 83918de8d7fSPeter Avalos 84018de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 841e9778795SPeter Avalos u_int status; 84218de8d7fSPeter Avalos 843e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &status)) != 0) 844e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 84536e94dc5SPeter Avalos error("Couldn't canonicalize: %s", fx2txt(status)); 846e9778795SPeter Avalos sshbuf_free(msg); 847856ea928SPeter Avalos return NULL; 84818de8d7fSPeter Avalos } else if (type != SSH2_FXP_NAME) 84918de8d7fSPeter Avalos fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 85018de8d7fSPeter Avalos SSH2_FXP_NAME, type); 85118de8d7fSPeter Avalos 852e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &count)) != 0) 853e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 85418de8d7fSPeter Avalos if (count != 1) 85518de8d7fSPeter Avalos fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); 85618de8d7fSPeter Avalos 857e9778795SPeter Avalos if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || 858e9778795SPeter Avalos (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || 859e9778795SPeter Avalos (r = decode_attrib(msg, &a)) != 0) 860e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 86118de8d7fSPeter Avalos 86299e85e0dSPeter Avalos debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename, 863e9778795SPeter Avalos (unsigned long)a.size); 86418de8d7fSPeter Avalos 86536e94dc5SPeter Avalos free(longname); 86618de8d7fSPeter Avalos 867e9778795SPeter Avalos sshbuf_free(msg); 86818de8d7fSPeter Avalos 86918de8d7fSPeter Avalos return(filename); 87018de8d7fSPeter Avalos } 87118de8d7fSPeter Avalos 87218de8d7fSPeter Avalos int 873e9778795SPeter Avalos do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath, 87436e94dc5SPeter Avalos int force_legacy) 87518de8d7fSPeter Avalos { 876e9778795SPeter Avalos struct sshbuf *msg; 87718de8d7fSPeter Avalos u_int status, id; 878e9778795SPeter Avalos int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy; 87918de8d7fSPeter Avalos 880e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 881e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 88218de8d7fSPeter Avalos 88318de8d7fSPeter Avalos /* Send rename request */ 88418de8d7fSPeter Avalos id = conn->msg_id++; 88536e94dc5SPeter Avalos if (use_ext) { 886e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 887e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 888e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, 889e9778795SPeter Avalos "posix-rename@openssh.com")) != 0) 890e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 89118de8d7fSPeter Avalos } else { 892e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 || 893e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0) 894e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 89518de8d7fSPeter Avalos } 896e9778795SPeter Avalos if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 || 897e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, newpath)) != 0) 898e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 899e9778795SPeter Avalos send_msg(conn, msg); 90018de8d7fSPeter Avalos debug3("Sent message %s \"%s\" -> \"%s\"", 901e9778795SPeter Avalos use_ext ? "posix-rename@openssh.com" : 902e9778795SPeter Avalos "SSH2_FXP_RENAME", oldpath, newpath); 903e9778795SPeter Avalos sshbuf_free(msg); 90418de8d7fSPeter Avalos 9059f304aafSPeter Avalos status = get_status(conn, id); 90618de8d7fSPeter Avalos if (status != SSH2_FX_OK) 90718de8d7fSPeter Avalos error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, 90818de8d7fSPeter Avalos newpath, fx2txt(status)); 90918de8d7fSPeter Avalos 910e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 91118de8d7fSPeter Avalos } 91218de8d7fSPeter Avalos 91318de8d7fSPeter Avalos int 914e9778795SPeter Avalos do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) 9159f304aafSPeter Avalos { 916e9778795SPeter Avalos struct sshbuf *msg; 9179f304aafSPeter Avalos u_int status, id; 918e9778795SPeter Avalos int r; 9199f304aafSPeter Avalos 9209f304aafSPeter Avalos if ((conn->exts & SFTP_EXT_HARDLINK) == 0) { 9219f304aafSPeter Avalos error("Server does not support hardlink@openssh.com extension"); 9229f304aafSPeter Avalos return -1; 9239f304aafSPeter Avalos } 9249f304aafSPeter Avalos 925e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 926e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 92799e85e0dSPeter Avalos 92899e85e0dSPeter Avalos /* Send link request */ 92999e85e0dSPeter Avalos id = conn->msg_id++; 930e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 931e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 932e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 || 933e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, oldpath)) != 0 || 934e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, newpath)) != 0) 935e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 936e9778795SPeter Avalos send_msg(conn, msg); 9379f304aafSPeter Avalos debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"", 9389f304aafSPeter Avalos oldpath, newpath); 939e9778795SPeter Avalos sshbuf_free(msg); 9409f304aafSPeter Avalos 9419f304aafSPeter Avalos status = get_status(conn, id); 9429f304aafSPeter Avalos if (status != SSH2_FX_OK) 9439f304aafSPeter Avalos error("Couldn't link file \"%s\" to \"%s\": %s", oldpath, 9449f304aafSPeter Avalos newpath, fx2txt(status)); 9459f304aafSPeter Avalos 946e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 9479f304aafSPeter Avalos } 9489f304aafSPeter Avalos 9499f304aafSPeter Avalos int 950e9778795SPeter Avalos do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) 95118de8d7fSPeter Avalos { 952e9778795SPeter Avalos struct sshbuf *msg; 95318de8d7fSPeter Avalos u_int status, id; 954e9778795SPeter Avalos int r; 95518de8d7fSPeter Avalos 95618de8d7fSPeter Avalos if (conn->version < 3) { 95718de8d7fSPeter Avalos error("This server does not support the symlink operation"); 95818de8d7fSPeter Avalos return(SSH2_FX_OP_UNSUPPORTED); 95918de8d7fSPeter Avalos } 96018de8d7fSPeter Avalos 961e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 962e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 96318de8d7fSPeter Avalos 96418de8d7fSPeter Avalos /* Send symlink request */ 96518de8d7fSPeter Avalos id = conn->msg_id++; 966e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 || 967e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 968e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, oldpath)) != 0 || 969e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, newpath)) != 0) 970e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 971e9778795SPeter Avalos send_msg(conn, msg); 97218de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, 97318de8d7fSPeter Avalos newpath); 974e9778795SPeter Avalos sshbuf_free(msg); 97518de8d7fSPeter Avalos 9769f304aafSPeter Avalos status = get_status(conn, id); 97718de8d7fSPeter Avalos if (status != SSH2_FX_OK) 97818de8d7fSPeter Avalos error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, 97918de8d7fSPeter Avalos newpath, fx2txt(status)); 98018de8d7fSPeter Avalos 981e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 98218de8d7fSPeter Avalos } 98318de8d7fSPeter Avalos 98436e94dc5SPeter Avalos int 985e9778795SPeter Avalos do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len) 98636e94dc5SPeter Avalos { 987e9778795SPeter Avalos struct sshbuf *msg; 98836e94dc5SPeter Avalos u_int status, id; 989e9778795SPeter Avalos int r; 99036e94dc5SPeter Avalos 99136e94dc5SPeter Avalos /* Silently return if the extension is not supported */ 99236e94dc5SPeter Avalos if ((conn->exts & SFTP_EXT_FSYNC) == 0) 99336e94dc5SPeter Avalos return -1; 99436e94dc5SPeter Avalos 99536e94dc5SPeter Avalos /* Send fsync request */ 996e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 997e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 99836e94dc5SPeter Avalos id = conn->msg_id++; 999e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1000e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 1001e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 || 1002e9778795SPeter Avalos (r = sshbuf_put_string(msg, handle, handle_len)) != 0) 1003e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1004e9778795SPeter Avalos send_msg(conn, msg); 100536e94dc5SPeter Avalos debug3("Sent message fsync@openssh.com I:%u", id); 1006e9778795SPeter Avalos sshbuf_free(msg); 100736e94dc5SPeter Avalos 100836e94dc5SPeter Avalos status = get_status(conn, id); 100936e94dc5SPeter Avalos if (status != SSH2_FX_OK) 101036e94dc5SPeter Avalos error("Couldn't sync file: %s", fx2txt(status)); 101136e94dc5SPeter Avalos 101236e94dc5SPeter Avalos return status; 101336e94dc5SPeter Avalos } 101436e94dc5SPeter Avalos 101518de8d7fSPeter Avalos #ifdef notyet 101618de8d7fSPeter Avalos char * 1017e9778795SPeter Avalos do_readlink(struct sftp_conn *conn, const char *path) 101818de8d7fSPeter Avalos { 1019e9778795SPeter Avalos struct sshbuf *msg; 1020e9778795SPeter Avalos u_int expected_id, count, id; 102118de8d7fSPeter Avalos char *filename, *longname; 1022e9778795SPeter Avalos Attrib a; 1023e9778795SPeter Avalos u_char type; 1024e9778795SPeter Avalos int r; 102518de8d7fSPeter Avalos 102618de8d7fSPeter Avalos expected_id = id = conn->msg_id++; 10279f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path)); 102818de8d7fSPeter Avalos 1029e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 1030e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 103118de8d7fSPeter Avalos 1032e9778795SPeter Avalos get_msg(conn, msg); 1033e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 1034e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &id)) != 0) 1035e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 103618de8d7fSPeter Avalos 103718de8d7fSPeter Avalos if (id != expected_id) 103818de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 103918de8d7fSPeter Avalos 104018de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 1041e9778795SPeter Avalos u_int status; 104218de8d7fSPeter Avalos 1043e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &status)) != 0) 1044e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 104518de8d7fSPeter Avalos error("Couldn't readlink: %s", fx2txt(status)); 1046e9778795SPeter Avalos sshbuf_free(msg); 104718de8d7fSPeter Avalos return(NULL); 104818de8d7fSPeter Avalos } else if (type != SSH2_FXP_NAME) 104918de8d7fSPeter Avalos fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 105018de8d7fSPeter Avalos SSH2_FXP_NAME, type); 105118de8d7fSPeter Avalos 1052e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &count)) != 0) 1053e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 105418de8d7fSPeter Avalos if (count != 1) 105518de8d7fSPeter Avalos fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); 105618de8d7fSPeter Avalos 1057e9778795SPeter Avalos if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || 1058e9778795SPeter Avalos (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || 1059e9778795SPeter Avalos (r = decode_attrib(msg, &a)) != 0) 1060e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 106118de8d7fSPeter Avalos 106218de8d7fSPeter Avalos debug3("SSH_FXP_READLINK %s -> %s", path, filename); 106318de8d7fSPeter Avalos 106436e94dc5SPeter Avalos free(longname); 106518de8d7fSPeter Avalos 1066e9778795SPeter Avalos sshbuf_free(msg); 106718de8d7fSPeter Avalos 1068e9778795SPeter Avalos return filename; 106918de8d7fSPeter Avalos } 107018de8d7fSPeter Avalos #endif 107118de8d7fSPeter Avalos 107218de8d7fSPeter Avalos int 107318de8d7fSPeter Avalos do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, 107418de8d7fSPeter Avalos int quiet) 107518de8d7fSPeter Avalos { 1076e9778795SPeter Avalos struct sshbuf *msg; 107718de8d7fSPeter Avalos u_int id; 1078e9778795SPeter Avalos int r; 107918de8d7fSPeter Avalos 108018de8d7fSPeter Avalos if ((conn->exts & SFTP_EXT_STATVFS) == 0) { 108118de8d7fSPeter Avalos error("Server does not support statvfs@openssh.com extension"); 108218de8d7fSPeter Avalos return -1; 108318de8d7fSPeter Avalos } 108418de8d7fSPeter Avalos 108518de8d7fSPeter Avalos id = conn->msg_id++; 108618de8d7fSPeter Avalos 1087e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 1088e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 1089e9778795SPeter Avalos sshbuf_reset(msg); 1090e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1091e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 1092e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || 1093e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, path)) != 0) 1094e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1095e9778795SPeter Avalos send_msg(conn, msg); 1096e9778795SPeter Avalos sshbuf_free(msg); 109718de8d7fSPeter Avalos 10989f304aafSPeter Avalos return get_decode_statvfs(conn, st, id, quiet); 109918de8d7fSPeter Avalos } 110018de8d7fSPeter Avalos 110118de8d7fSPeter Avalos #ifdef notyet 110218de8d7fSPeter Avalos int 1103e9778795SPeter Avalos do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len, 110418de8d7fSPeter Avalos struct sftp_statvfs *st, int quiet) 110518de8d7fSPeter Avalos { 1106e9778795SPeter Avalos struct sshbuf *msg; 110718de8d7fSPeter Avalos u_int id; 110818de8d7fSPeter Avalos 110918de8d7fSPeter Avalos if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) { 111018de8d7fSPeter Avalos error("Server does not support fstatvfs@openssh.com extension"); 111118de8d7fSPeter Avalos return -1; 111218de8d7fSPeter Avalos } 111318de8d7fSPeter Avalos 111418de8d7fSPeter Avalos id = conn->msg_id++; 111518de8d7fSPeter Avalos 1116e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 1117e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 1118e9778795SPeter Avalos sshbuf_reset(msg); 1119e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1120e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 1121e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || 1122e9778795SPeter Avalos (r = sshbuf_put_string(msg, handle, handle_len)) != 0) 1123e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1124e9778795SPeter Avalos send_msg(conn, msg); 1125e9778795SPeter Avalos sshbuf_free(msg); 112618de8d7fSPeter Avalos 11279f304aafSPeter Avalos return get_decode_statvfs(conn, st, id, quiet); 112818de8d7fSPeter Avalos } 112918de8d7fSPeter Avalos #endif 113018de8d7fSPeter Avalos 113118de8d7fSPeter Avalos static void 11329f304aafSPeter Avalos send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset, 1133e9778795SPeter Avalos u_int len, const u_char *handle, u_int handle_len) 113418de8d7fSPeter Avalos { 1135e9778795SPeter Avalos struct sshbuf *msg; 1136e9778795SPeter Avalos int r; 113718de8d7fSPeter Avalos 1138e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 1139e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 1140e9778795SPeter Avalos sshbuf_reset(msg); 1141e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 || 1142e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 1143e9778795SPeter Avalos (r = sshbuf_put_string(msg, handle, handle_len)) != 0 || 1144e9778795SPeter Avalos (r = sshbuf_put_u64(msg, offset)) != 0 || 1145e9778795SPeter Avalos (r = sshbuf_put_u32(msg, len)) != 0) 1146e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1147e9778795SPeter Avalos send_msg(conn, msg); 1148e9778795SPeter Avalos sshbuf_free(msg); 114918de8d7fSPeter Avalos } 115018de8d7fSPeter Avalos 115118de8d7fSPeter Avalos int 1152e9778795SPeter Avalos do_download(struct sftp_conn *conn, const char *remote_path, 1153e9778795SPeter Avalos const char *local_path, Attrib *a, int preserve_flag, int resume_flag, 1154e9778795SPeter Avalos int fsync_flag) 115518de8d7fSPeter Avalos { 1156856ea928SPeter Avalos Attrib junk; 1157e9778795SPeter Avalos struct sshbuf *msg; 1158e9778795SPeter Avalos u_char *handle; 1159e9778795SPeter Avalos int local_fd = -1, write_error; 1160e9778795SPeter Avalos int read_error, write_errno, reordered = 0, r; 116136e94dc5SPeter Avalos u_int64_t offset = 0, size, highwater; 1162e9778795SPeter Avalos u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK; 116318de8d7fSPeter Avalos off_t progress_counter; 1164e9778795SPeter Avalos size_t handle_len; 116536e94dc5SPeter Avalos struct stat st; 116618de8d7fSPeter Avalos struct request { 116718de8d7fSPeter Avalos u_int id; 1168e9778795SPeter Avalos size_t len; 116918de8d7fSPeter Avalos u_int64_t offset; 117018de8d7fSPeter Avalos TAILQ_ENTRY(request) tq; 117118de8d7fSPeter Avalos }; 117218de8d7fSPeter Avalos TAILQ_HEAD(reqhead, request) requests; 117318de8d7fSPeter Avalos struct request *req; 1174e9778795SPeter Avalos u_char type; 117518de8d7fSPeter Avalos 117618de8d7fSPeter Avalos TAILQ_INIT(&requests); 117718de8d7fSPeter Avalos 1178856ea928SPeter Avalos if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) 1179856ea928SPeter Avalos return -1; 118018de8d7fSPeter Avalos 118118de8d7fSPeter Avalos /* Do not preserve set[ug]id here, as we do not preserve ownership */ 118218de8d7fSPeter Avalos if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 118318de8d7fSPeter Avalos mode = a->perm & 0777; 118418de8d7fSPeter Avalos else 118518de8d7fSPeter Avalos mode = 0666; 118618de8d7fSPeter Avalos 118718de8d7fSPeter Avalos if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 118818de8d7fSPeter Avalos (!S_ISREG(a->perm))) { 118918de8d7fSPeter Avalos error("Cannot download non-regular file: %s", remote_path); 119018de8d7fSPeter Avalos return(-1); 119118de8d7fSPeter Avalos } 119218de8d7fSPeter Avalos 119318de8d7fSPeter Avalos if (a->flags & SSH2_FILEXFER_ATTR_SIZE) 119418de8d7fSPeter Avalos size = a->size; 119518de8d7fSPeter Avalos else 119618de8d7fSPeter Avalos size = 0; 119718de8d7fSPeter Avalos 119818de8d7fSPeter Avalos buflen = conn->transfer_buflen; 1199e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 1200e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 1201e9778795SPeter Avalos 1202e9778795SPeter Avalos attrib_clear(&junk); /* Send empty attributes */ 120318de8d7fSPeter Avalos 120418de8d7fSPeter Avalos /* Send open request */ 120518de8d7fSPeter Avalos id = conn->msg_id++; 1206e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || 1207e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 1208e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, remote_path)) != 0 || 1209e9778795SPeter Avalos (r = sshbuf_put_u32(msg, SSH2_FXF_READ)) != 0 || 1210e9778795SPeter Avalos (r = encode_attrib(msg, &junk)) != 0) 1211e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1212e9778795SPeter Avalos send_msg(conn, msg); 121318de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 121418de8d7fSPeter Avalos 12159f304aafSPeter Avalos handle = get_handle(conn, id, &handle_len, 1216856ea928SPeter Avalos "remote open(\"%s\")", remote_path); 121718de8d7fSPeter Avalos if (handle == NULL) { 1218e9778795SPeter Avalos sshbuf_free(msg); 121918de8d7fSPeter Avalos return(-1); 122018de8d7fSPeter Avalos } 122118de8d7fSPeter Avalos 122236e94dc5SPeter Avalos local_fd = open(local_path, 122336e94dc5SPeter Avalos O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR); 122418de8d7fSPeter Avalos if (local_fd == -1) { 122518de8d7fSPeter Avalos error("Couldn't open local file \"%s\" for writing: %s", 122618de8d7fSPeter Avalos local_path, strerror(errno)); 122736e94dc5SPeter Avalos goto fail; 122836e94dc5SPeter Avalos } 122936e94dc5SPeter Avalos offset = highwater = 0; 123036e94dc5SPeter Avalos if (resume_flag) { 123136e94dc5SPeter Avalos if (fstat(local_fd, &st) == -1) { 123236e94dc5SPeter Avalos error("Unable to stat local file \"%s\": %s", 123336e94dc5SPeter Avalos local_path, strerror(errno)); 123436e94dc5SPeter Avalos goto fail; 123536e94dc5SPeter Avalos } 123636e94dc5SPeter Avalos if (st.st_size < 0) { 123736e94dc5SPeter Avalos error("\"%s\" has negative size", local_path); 123836e94dc5SPeter Avalos goto fail; 123936e94dc5SPeter Avalos } 124036e94dc5SPeter Avalos if ((u_int64_t)st.st_size > size) { 124136e94dc5SPeter Avalos error("Unable to resume download of \"%s\": " 124236e94dc5SPeter Avalos "local file is larger than remote", local_path); 124336e94dc5SPeter Avalos fail: 124418de8d7fSPeter Avalos do_close(conn, handle, handle_len); 1245e9778795SPeter Avalos sshbuf_free(msg); 124636e94dc5SPeter Avalos free(handle); 124736e94dc5SPeter Avalos if (local_fd != -1) 124836e94dc5SPeter Avalos close(local_fd); 124936e94dc5SPeter Avalos return -1; 125036e94dc5SPeter Avalos } 125136e94dc5SPeter Avalos offset = highwater = st.st_size; 125218de8d7fSPeter Avalos } 125318de8d7fSPeter Avalos 125418de8d7fSPeter Avalos /* Read from remote and write to local */ 125536e94dc5SPeter Avalos write_error = read_error = write_errno = num_req = 0; 125618de8d7fSPeter Avalos max_req = 1; 125736e94dc5SPeter Avalos progress_counter = offset; 125818de8d7fSPeter Avalos 125918de8d7fSPeter Avalos if (showprogress && size != 0) 126018de8d7fSPeter Avalos start_progress_meter(remote_path, size, &progress_counter); 126118de8d7fSPeter Avalos 126218de8d7fSPeter Avalos while (num_req > 0 || max_req > 0) { 1263e9778795SPeter Avalos u_char *data; 1264e9778795SPeter Avalos size_t len; 126518de8d7fSPeter Avalos 126618de8d7fSPeter Avalos /* 126718de8d7fSPeter Avalos * Simulate EOF on interrupt: stop sending new requests and 126818de8d7fSPeter Avalos * allow outstanding requests to drain gracefully 126918de8d7fSPeter Avalos */ 127018de8d7fSPeter Avalos if (interrupted) { 127118de8d7fSPeter Avalos if (num_req == 0) /* If we haven't started yet... */ 127218de8d7fSPeter Avalos break; 127318de8d7fSPeter Avalos max_req = 0; 127418de8d7fSPeter Avalos } 127518de8d7fSPeter Avalos 127618de8d7fSPeter Avalos /* Send some more requests */ 127718de8d7fSPeter Avalos while (num_req < max_req) { 127818de8d7fSPeter Avalos debug3("Request range %llu -> %llu (%d/%d)", 127918de8d7fSPeter Avalos (unsigned long long)offset, 128018de8d7fSPeter Avalos (unsigned long long)offset + buflen - 1, 128118de8d7fSPeter Avalos num_req, max_req); 128236e94dc5SPeter Avalos req = xcalloc(1, sizeof(*req)); 128318de8d7fSPeter Avalos req->id = conn->msg_id++; 128418de8d7fSPeter Avalos req->len = buflen; 128518de8d7fSPeter Avalos req->offset = offset; 128618de8d7fSPeter Avalos offset += buflen; 128718de8d7fSPeter Avalos num_req++; 128818de8d7fSPeter Avalos TAILQ_INSERT_TAIL(&requests, req, tq); 12899f304aafSPeter Avalos send_read_request(conn, req->id, req->offset, 129018de8d7fSPeter Avalos req->len, handle, handle_len); 129118de8d7fSPeter Avalos } 129218de8d7fSPeter Avalos 1293e9778795SPeter Avalos sshbuf_reset(msg); 1294e9778795SPeter Avalos get_msg(conn, msg); 1295e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 1296e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &id)) != 0) 1297e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 129818de8d7fSPeter Avalos debug3("Received reply T:%u I:%u R:%d", type, id, max_req); 129918de8d7fSPeter Avalos 130018de8d7fSPeter Avalos /* Find the request in our queue */ 130118de8d7fSPeter Avalos for (req = TAILQ_FIRST(&requests); 130218de8d7fSPeter Avalos req != NULL && req->id != id; 130318de8d7fSPeter Avalos req = TAILQ_NEXT(req, tq)) 130418de8d7fSPeter Avalos ; 130518de8d7fSPeter Avalos if (req == NULL) 130618de8d7fSPeter Avalos fatal("Unexpected reply %u", id); 130718de8d7fSPeter Avalos 130818de8d7fSPeter Avalos switch (type) { 130918de8d7fSPeter Avalos case SSH2_FXP_STATUS: 1310e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &status)) != 0) 1311e9778795SPeter Avalos fatal("%s: buffer error: %s", 1312e9778795SPeter Avalos __func__, ssh_err(r)); 131318de8d7fSPeter Avalos if (status != SSH2_FX_EOF) 131418de8d7fSPeter Avalos read_error = 1; 131518de8d7fSPeter Avalos max_req = 0; 131618de8d7fSPeter Avalos TAILQ_REMOVE(&requests, req, tq); 131736e94dc5SPeter Avalos free(req); 131818de8d7fSPeter Avalos num_req--; 131918de8d7fSPeter Avalos break; 132018de8d7fSPeter Avalos case SSH2_FXP_DATA: 1321e9778795SPeter Avalos if ((r = sshbuf_get_string(msg, &data, &len)) != 0) 1322e9778795SPeter Avalos fatal("%s: buffer error: %s", 1323e9778795SPeter Avalos __func__, ssh_err(r)); 132418de8d7fSPeter Avalos debug3("Received data %llu -> %llu", 132518de8d7fSPeter Avalos (unsigned long long)req->offset, 132618de8d7fSPeter Avalos (unsigned long long)req->offset + len - 1); 132718de8d7fSPeter Avalos if (len > req->len) 132818de8d7fSPeter Avalos fatal("Received more data than asked for " 1329e9778795SPeter Avalos "%zu > %zu", len, req->len); 133018de8d7fSPeter Avalos if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || 133118de8d7fSPeter Avalos atomicio(vwrite, local_fd, data, len) != len) && 133218de8d7fSPeter Avalos !write_error) { 133318de8d7fSPeter Avalos write_errno = errno; 133418de8d7fSPeter Avalos write_error = 1; 133518de8d7fSPeter Avalos max_req = 0; 133618de8d7fSPeter Avalos } 133736e94dc5SPeter Avalos else if (!reordered && req->offset <= highwater) 133836e94dc5SPeter Avalos highwater = req->offset + len; 133936e94dc5SPeter Avalos else if (!reordered && req->offset > highwater) 134036e94dc5SPeter Avalos reordered = 1; 134118de8d7fSPeter Avalos progress_counter += len; 134236e94dc5SPeter Avalos free(data); 134318de8d7fSPeter Avalos 134418de8d7fSPeter Avalos if (len == req->len) { 134518de8d7fSPeter Avalos TAILQ_REMOVE(&requests, req, tq); 134636e94dc5SPeter Avalos free(req); 134718de8d7fSPeter Avalos num_req--; 134818de8d7fSPeter Avalos } else { 134918de8d7fSPeter Avalos /* Resend the request for the missing data */ 135018de8d7fSPeter Avalos debug3("Short data block, re-requesting " 135118de8d7fSPeter Avalos "%llu -> %llu (%2d)", 135218de8d7fSPeter Avalos (unsigned long long)req->offset + len, 135318de8d7fSPeter Avalos (unsigned long long)req->offset + 135418de8d7fSPeter Avalos req->len - 1, num_req); 135518de8d7fSPeter Avalos req->id = conn->msg_id++; 135618de8d7fSPeter Avalos req->len -= len; 135718de8d7fSPeter Avalos req->offset += len; 13589f304aafSPeter Avalos send_read_request(conn, req->id, 135918de8d7fSPeter Avalos req->offset, req->len, handle, handle_len); 136018de8d7fSPeter Avalos /* Reduce the request size */ 136118de8d7fSPeter Avalos if (len < buflen) 1362*ce74bacaSMatthew Dillon buflen = MAXIMUM(MIN_READ_SIZE, len); 136318de8d7fSPeter Avalos } 136418de8d7fSPeter Avalos if (max_req > 0) { /* max_req = 0 iff EOF received */ 136518de8d7fSPeter Avalos if (size > 0 && offset > size) { 136618de8d7fSPeter Avalos /* Only one request at a time 136718de8d7fSPeter Avalos * after the expected EOF */ 136818de8d7fSPeter Avalos debug3("Finish at %llu (%2d)", 136918de8d7fSPeter Avalos (unsigned long long)offset, 137018de8d7fSPeter Avalos num_req); 137118de8d7fSPeter Avalos max_req = 1; 137218de8d7fSPeter Avalos } else if (max_req <= conn->num_requests) { 137318de8d7fSPeter Avalos ++max_req; 137418de8d7fSPeter Avalos } 137518de8d7fSPeter Avalos } 137618de8d7fSPeter Avalos break; 137718de8d7fSPeter Avalos default: 137818de8d7fSPeter Avalos fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", 137918de8d7fSPeter Avalos SSH2_FXP_DATA, type); 138018de8d7fSPeter Avalos } 138118de8d7fSPeter Avalos } 138218de8d7fSPeter Avalos 138318de8d7fSPeter Avalos if (showprogress && size) 138418de8d7fSPeter Avalos stop_progress_meter(); 138518de8d7fSPeter Avalos 138618de8d7fSPeter Avalos /* Sanity check */ 138718de8d7fSPeter Avalos if (TAILQ_FIRST(&requests) != NULL) 138818de8d7fSPeter Avalos fatal("Transfer complete, but requests still in queue"); 138936e94dc5SPeter Avalos /* Truncate at highest contiguous point to avoid holes on interrupt */ 139036e94dc5SPeter Avalos if (read_error || write_error || interrupted) { 139136e94dc5SPeter Avalos if (reordered && resume_flag) { 139236e94dc5SPeter Avalos error("Unable to resume download of \"%s\": " 139336e94dc5SPeter Avalos "server reordered requests", local_path); 139436e94dc5SPeter Avalos } 139536e94dc5SPeter Avalos debug("truncating at %llu", (unsigned long long)highwater); 1396e9778795SPeter Avalos if (ftruncate(local_fd, highwater) == -1) 1397e9778795SPeter Avalos error("ftruncate \"%s\": %s", local_path, 1398e9778795SPeter Avalos strerror(errno)); 139936e94dc5SPeter Avalos } 140018de8d7fSPeter Avalos if (read_error) { 140118de8d7fSPeter Avalos error("Couldn't read from remote file \"%s\" : %s", 140218de8d7fSPeter Avalos remote_path, fx2txt(status)); 140336e94dc5SPeter Avalos status = -1; 140418de8d7fSPeter Avalos do_close(conn, handle, handle_len); 140518de8d7fSPeter Avalos } else if (write_error) { 140618de8d7fSPeter Avalos error("Couldn't write to \"%s\": %s", local_path, 140718de8d7fSPeter Avalos strerror(write_errno)); 1408e9778795SPeter Avalos status = SSH2_FX_FAILURE; 140918de8d7fSPeter Avalos do_close(conn, handle, handle_len); 141018de8d7fSPeter Avalos } else { 1411e9778795SPeter Avalos if (do_close(conn, handle, handle_len) != 0 || interrupted) 1412e9778795SPeter Avalos status = SSH2_FX_FAILURE; 1413e9778795SPeter Avalos else 1414e9778795SPeter Avalos status = SSH2_FX_OK; 141518de8d7fSPeter Avalos /* Override umask and utimes if asked */ 141618de8d7fSPeter Avalos #ifdef HAVE_FCHMOD 141736e94dc5SPeter Avalos if (preserve_flag && fchmod(local_fd, mode) == -1) 141818de8d7fSPeter Avalos #else 141936e94dc5SPeter Avalos if (preserve_flag && chmod(local_path, mode) == -1) 142018de8d7fSPeter Avalos #endif /* HAVE_FCHMOD */ 142118de8d7fSPeter Avalos error("Couldn't set mode on \"%s\": %s", local_path, 142218de8d7fSPeter Avalos strerror(errno)); 142336e94dc5SPeter Avalos if (preserve_flag && 142436e94dc5SPeter Avalos (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { 142518de8d7fSPeter Avalos struct timeval tv[2]; 142618de8d7fSPeter Avalos tv[0].tv_sec = a->atime; 142718de8d7fSPeter Avalos tv[1].tv_sec = a->mtime; 142818de8d7fSPeter Avalos tv[0].tv_usec = tv[1].tv_usec = 0; 142918de8d7fSPeter Avalos if (utimes(local_path, tv) == -1) 143018de8d7fSPeter Avalos error("Can't set times on \"%s\": %s", 143118de8d7fSPeter Avalos local_path, strerror(errno)); 143218de8d7fSPeter Avalos } 143336e94dc5SPeter Avalos if (fsync_flag) { 143436e94dc5SPeter Avalos debug("syncing \"%s\"", local_path); 143536e94dc5SPeter Avalos if (fsync(local_fd) == -1) 143636e94dc5SPeter Avalos error("Couldn't sync file \"%s\": %s", 143736e94dc5SPeter Avalos local_path, strerror(errno)); 143836e94dc5SPeter Avalos } 143918de8d7fSPeter Avalos } 144018de8d7fSPeter Avalos close(local_fd); 1441e9778795SPeter Avalos sshbuf_free(msg); 144236e94dc5SPeter Avalos free(handle); 144318de8d7fSPeter Avalos 144418de8d7fSPeter Avalos return(status); 144518de8d7fSPeter Avalos } 144618de8d7fSPeter Avalos 1447856ea928SPeter Avalos static int 1448e9778795SPeter Avalos download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, 1449e9778795SPeter Avalos int depth, Attrib *dirattrib, int preserve_flag, int print_flag, 1450e9778795SPeter Avalos int resume_flag, int fsync_flag) 1451856ea928SPeter Avalos { 1452856ea928SPeter Avalos int i, ret = 0; 1453856ea928SPeter Avalos SFTP_DIRENT **dir_entries; 1454856ea928SPeter Avalos char *filename, *new_src, *new_dst; 1455856ea928SPeter Avalos mode_t mode = 0777; 1456856ea928SPeter Avalos 1457856ea928SPeter Avalos if (depth >= MAX_DIR_DEPTH) { 1458856ea928SPeter Avalos error("Maximum directory depth exceeded: %d levels", depth); 1459856ea928SPeter Avalos return -1; 1460856ea928SPeter Avalos } 1461856ea928SPeter Avalos 1462856ea928SPeter Avalos if (dirattrib == NULL && 1463856ea928SPeter Avalos (dirattrib = do_stat(conn, src, 1)) == NULL) { 1464856ea928SPeter Avalos error("Unable to stat remote directory \"%s\"", src); 1465856ea928SPeter Avalos return -1; 1466856ea928SPeter Avalos } 1467856ea928SPeter Avalos if (!S_ISDIR(dirattrib->perm)) { 1468856ea928SPeter Avalos error("\"%s\" is not a directory", src); 1469856ea928SPeter Avalos return -1; 1470856ea928SPeter Avalos } 147136e94dc5SPeter Avalos if (print_flag) 1472e9778795SPeter Avalos mprintf("Retrieving %s\n", src); 1473856ea928SPeter Avalos 1474856ea928SPeter Avalos if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 1475856ea928SPeter Avalos mode = dirattrib->perm & 01777; 1476856ea928SPeter Avalos else { 1477856ea928SPeter Avalos debug("Server did not send permissions for " 1478856ea928SPeter Avalos "directory \"%s\"", dst); 1479856ea928SPeter Avalos } 1480856ea928SPeter Avalos 1481856ea928SPeter Avalos if (mkdir(dst, mode) == -1 && errno != EEXIST) { 1482856ea928SPeter Avalos error("mkdir %s: %s", dst, strerror(errno)); 1483856ea928SPeter Avalos return -1; 1484856ea928SPeter Avalos } 1485856ea928SPeter Avalos 1486856ea928SPeter Avalos if (do_readdir(conn, src, &dir_entries) == -1) { 1487856ea928SPeter Avalos error("%s: Failed to get directory contents", src); 1488856ea928SPeter Avalos return -1; 1489856ea928SPeter Avalos } 1490856ea928SPeter Avalos 1491856ea928SPeter Avalos for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { 1492856ea928SPeter Avalos filename = dir_entries[i]->filename; 1493856ea928SPeter Avalos 1494856ea928SPeter Avalos new_dst = path_append(dst, filename); 1495856ea928SPeter Avalos new_src = path_append(src, filename); 1496856ea928SPeter Avalos 1497856ea928SPeter Avalos if (S_ISDIR(dir_entries[i]->a.perm)) { 1498856ea928SPeter Avalos if (strcmp(filename, ".") == 0 || 1499856ea928SPeter Avalos strcmp(filename, "..") == 0) 1500856ea928SPeter Avalos continue; 1501856ea928SPeter Avalos if (download_dir_internal(conn, new_src, new_dst, 150236e94dc5SPeter Avalos depth + 1, &(dir_entries[i]->a), preserve_flag, 150336e94dc5SPeter Avalos print_flag, resume_flag, fsync_flag) == -1) 1504856ea928SPeter Avalos ret = -1; 1505856ea928SPeter Avalos } else if (S_ISREG(dir_entries[i]->a.perm) ) { 1506856ea928SPeter Avalos if (do_download(conn, new_src, new_dst, 150736e94dc5SPeter Avalos &(dir_entries[i]->a), preserve_flag, 150836e94dc5SPeter Avalos resume_flag, fsync_flag) == -1) { 1509856ea928SPeter Avalos error("Download of file %s to %s failed", 1510856ea928SPeter Avalos new_src, new_dst); 1511856ea928SPeter Avalos ret = -1; 1512856ea928SPeter Avalos } 1513856ea928SPeter Avalos } else 1514856ea928SPeter Avalos logit("%s: not a regular file\n", new_src); 1515856ea928SPeter Avalos 151636e94dc5SPeter Avalos free(new_dst); 151736e94dc5SPeter Avalos free(new_src); 1518856ea928SPeter Avalos } 1519856ea928SPeter Avalos 152036e94dc5SPeter Avalos if (preserve_flag) { 1521856ea928SPeter Avalos if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 1522856ea928SPeter Avalos struct timeval tv[2]; 1523856ea928SPeter Avalos tv[0].tv_sec = dirattrib->atime; 1524856ea928SPeter Avalos tv[1].tv_sec = dirattrib->mtime; 1525856ea928SPeter Avalos tv[0].tv_usec = tv[1].tv_usec = 0; 1526856ea928SPeter Avalos if (utimes(dst, tv) == -1) 1527856ea928SPeter Avalos error("Can't set times on \"%s\": %s", 1528856ea928SPeter Avalos dst, strerror(errno)); 1529856ea928SPeter Avalos } else 1530856ea928SPeter Avalos debug("Server did not send times for directory " 1531856ea928SPeter Avalos "\"%s\"", dst); 1532856ea928SPeter Avalos } 1533856ea928SPeter Avalos 1534856ea928SPeter Avalos free_sftp_dirents(dir_entries); 1535856ea928SPeter Avalos 1536856ea928SPeter Avalos return ret; 1537856ea928SPeter Avalos } 1538856ea928SPeter Avalos 1539856ea928SPeter Avalos int 1540e9778795SPeter Avalos download_dir(struct sftp_conn *conn, const char *src, const char *dst, 1541e9778795SPeter Avalos Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, 1542e9778795SPeter Avalos int fsync_flag) 1543856ea928SPeter Avalos { 1544856ea928SPeter Avalos char *src_canon; 1545856ea928SPeter Avalos int ret; 1546856ea928SPeter Avalos 1547856ea928SPeter Avalos if ((src_canon = do_realpath(conn, src)) == NULL) { 154836e94dc5SPeter Avalos error("Unable to canonicalize path \"%s\"", src); 1549856ea928SPeter Avalos return -1; 1550856ea928SPeter Avalos } 1551856ea928SPeter Avalos 155236e94dc5SPeter Avalos ret = download_dir_internal(conn, src_canon, dst, 0, 155336e94dc5SPeter Avalos dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag); 155436e94dc5SPeter Avalos free(src_canon); 1555856ea928SPeter Avalos return ret; 1556856ea928SPeter Avalos } 1557856ea928SPeter Avalos 155818de8d7fSPeter Avalos int 1559e9778795SPeter Avalos do_upload(struct sftp_conn *conn, const char *local_path, 1560e9778795SPeter Avalos const char *remote_path, int preserve_flag, int resume, int fsync_flag) 156118de8d7fSPeter Avalos { 1562e9778795SPeter Avalos int r, local_fd; 1563e9778795SPeter Avalos u_int status = SSH2_FX_OK; 1564e9778795SPeter Avalos u_int id; 1565e9778795SPeter Avalos u_char type; 156636e94dc5SPeter Avalos off_t offset, progress_counter; 1567e9778795SPeter Avalos u_char *handle, *data; 1568e9778795SPeter Avalos struct sshbuf *msg; 156918de8d7fSPeter Avalos struct stat sb; 157036e94dc5SPeter Avalos Attrib a, *c = NULL; 157118de8d7fSPeter Avalos u_int32_t startid; 157218de8d7fSPeter Avalos u_int32_t ackid; 157318de8d7fSPeter Avalos struct outstanding_ack { 157418de8d7fSPeter Avalos u_int id; 157518de8d7fSPeter Avalos u_int len; 157618de8d7fSPeter Avalos off_t offset; 157718de8d7fSPeter Avalos TAILQ_ENTRY(outstanding_ack) tq; 157818de8d7fSPeter Avalos }; 157918de8d7fSPeter Avalos TAILQ_HEAD(ackhead, outstanding_ack) acks; 158018de8d7fSPeter Avalos struct outstanding_ack *ack = NULL; 1581e9778795SPeter Avalos size_t handle_len; 158218de8d7fSPeter Avalos 158318de8d7fSPeter Avalos TAILQ_INIT(&acks); 158418de8d7fSPeter Avalos 158518de8d7fSPeter Avalos if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { 158618de8d7fSPeter Avalos error("Couldn't open local file \"%s\" for reading: %s", 158718de8d7fSPeter Avalos local_path, strerror(errno)); 158818de8d7fSPeter Avalos return(-1); 158918de8d7fSPeter Avalos } 159018de8d7fSPeter Avalos if (fstat(local_fd, &sb) == -1) { 159118de8d7fSPeter Avalos error("Couldn't fstat local file \"%s\": %s", 159218de8d7fSPeter Avalos local_path, strerror(errno)); 159318de8d7fSPeter Avalos close(local_fd); 159418de8d7fSPeter Avalos return(-1); 159518de8d7fSPeter Avalos } 159618de8d7fSPeter Avalos if (!S_ISREG(sb.st_mode)) { 159718de8d7fSPeter Avalos error("%s is not a regular file", local_path); 159818de8d7fSPeter Avalos close(local_fd); 159918de8d7fSPeter Avalos return(-1); 160018de8d7fSPeter Avalos } 160118de8d7fSPeter Avalos stat_to_attrib(&sb, &a); 160218de8d7fSPeter Avalos 160318de8d7fSPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 160418de8d7fSPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 160518de8d7fSPeter Avalos a.perm &= 0777; 160636e94dc5SPeter Avalos if (!preserve_flag) 160718de8d7fSPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 160818de8d7fSPeter Avalos 160936e94dc5SPeter Avalos if (resume) { 161036e94dc5SPeter Avalos /* Get remote file size if it exists */ 161136e94dc5SPeter Avalos if ((c = do_stat(conn, remote_path, 0)) == NULL) { 161236e94dc5SPeter Avalos close(local_fd); 161336e94dc5SPeter Avalos return -1; 161436e94dc5SPeter Avalos } 161536e94dc5SPeter Avalos 161636e94dc5SPeter Avalos if ((off_t)c->size >= sb.st_size) { 161736e94dc5SPeter Avalos error("destination file bigger or same size as " 161836e94dc5SPeter Avalos "source file"); 161936e94dc5SPeter Avalos close(local_fd); 162036e94dc5SPeter Avalos return -1; 162136e94dc5SPeter Avalos } 162236e94dc5SPeter Avalos 162336e94dc5SPeter Avalos if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) { 162436e94dc5SPeter Avalos close(local_fd); 162536e94dc5SPeter Avalos return -1; 162636e94dc5SPeter Avalos } 162736e94dc5SPeter Avalos } 162836e94dc5SPeter Avalos 1629e9778795SPeter Avalos if ((msg = sshbuf_new()) == NULL) 1630e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 163118de8d7fSPeter Avalos 163218de8d7fSPeter Avalos /* Send open request */ 163318de8d7fSPeter Avalos id = conn->msg_id++; 1634e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || 1635e9778795SPeter Avalos (r = sshbuf_put_u32(msg, id)) != 0 || 1636e9778795SPeter Avalos (r = sshbuf_put_cstring(msg, remote_path)) != 0 || 1637e9778795SPeter Avalos (r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT| 1638e9778795SPeter Avalos (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC))) != 0 || 1639e9778795SPeter Avalos (r = encode_attrib(msg, &a)) != 0) 1640e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1641e9778795SPeter Avalos send_msg(conn, msg); 164218de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 164318de8d7fSPeter Avalos 1644e9778795SPeter Avalos sshbuf_reset(msg); 164518de8d7fSPeter Avalos 16469f304aafSPeter Avalos handle = get_handle(conn, id, &handle_len, 1647856ea928SPeter Avalos "remote open(\"%s\")", remote_path); 164818de8d7fSPeter Avalos if (handle == NULL) { 164918de8d7fSPeter Avalos close(local_fd); 1650e9778795SPeter Avalos sshbuf_free(msg); 165118de8d7fSPeter Avalos return -1; 165218de8d7fSPeter Avalos } 165318de8d7fSPeter Avalos 165418de8d7fSPeter Avalos startid = ackid = id + 1; 165518de8d7fSPeter Avalos data = xmalloc(conn->transfer_buflen); 165618de8d7fSPeter Avalos 165718de8d7fSPeter Avalos /* Read from local and write to remote */ 165836e94dc5SPeter Avalos offset = progress_counter = (resume ? c->size : 0); 165918de8d7fSPeter Avalos if (showprogress) 166036e94dc5SPeter Avalos start_progress_meter(local_path, sb.st_size, 166136e94dc5SPeter Avalos &progress_counter); 166218de8d7fSPeter Avalos 166318de8d7fSPeter Avalos for (;;) { 166418de8d7fSPeter Avalos int len; 166518de8d7fSPeter Avalos 166618de8d7fSPeter Avalos /* 166718de8d7fSPeter Avalos * Can't use atomicio here because it returns 0 on EOF, 166818de8d7fSPeter Avalos * thus losing the last block of the file. 166918de8d7fSPeter Avalos * Simulate an EOF on interrupt, allowing ACKs from the 167018de8d7fSPeter Avalos * server to drain. 167118de8d7fSPeter Avalos */ 167218de8d7fSPeter Avalos if (interrupted || status != SSH2_FX_OK) 167318de8d7fSPeter Avalos len = 0; 167418de8d7fSPeter Avalos else do 167518de8d7fSPeter Avalos len = read(local_fd, data, conn->transfer_buflen); 167618de8d7fSPeter Avalos while ((len == -1) && 167718de8d7fSPeter Avalos (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); 167818de8d7fSPeter Avalos 167918de8d7fSPeter Avalos if (len == -1) 168018de8d7fSPeter Avalos fatal("Couldn't read from \"%s\": %s", local_path, 168118de8d7fSPeter Avalos strerror(errno)); 168218de8d7fSPeter Avalos 168318de8d7fSPeter Avalos if (len != 0) { 168436e94dc5SPeter Avalos ack = xcalloc(1, sizeof(*ack)); 168518de8d7fSPeter Avalos ack->id = ++id; 168618de8d7fSPeter Avalos ack->offset = offset; 168718de8d7fSPeter Avalos ack->len = len; 168818de8d7fSPeter Avalos TAILQ_INSERT_TAIL(&acks, ack, tq); 168918de8d7fSPeter Avalos 1690e9778795SPeter Avalos sshbuf_reset(msg); 1691e9778795SPeter Avalos if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 || 1692e9778795SPeter Avalos (r = sshbuf_put_u32(msg, ack->id)) != 0 || 1693e9778795SPeter Avalos (r = sshbuf_put_string(msg, handle, 1694e9778795SPeter Avalos handle_len)) != 0 || 1695e9778795SPeter Avalos (r = sshbuf_put_u64(msg, offset)) != 0 || 1696e9778795SPeter Avalos (r = sshbuf_put_string(msg, data, len)) != 0) 1697e9778795SPeter Avalos fatal("%s: buffer error: %s", 1698e9778795SPeter Avalos __func__, ssh_err(r)); 1699e9778795SPeter Avalos send_msg(conn, msg); 170018de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", 170118de8d7fSPeter Avalos id, (unsigned long long)offset, len); 170218de8d7fSPeter Avalos } else if (TAILQ_FIRST(&acks) == NULL) 170318de8d7fSPeter Avalos break; 170418de8d7fSPeter Avalos 170518de8d7fSPeter Avalos if (ack == NULL) 170618de8d7fSPeter Avalos fatal("Unexpected ACK %u", id); 170718de8d7fSPeter Avalos 170818de8d7fSPeter Avalos if (id == startid || len == 0 || 170918de8d7fSPeter Avalos id - ackid >= conn->num_requests) { 1710e9778795SPeter Avalos u_int rid; 171118de8d7fSPeter Avalos 1712e9778795SPeter Avalos sshbuf_reset(msg); 1713e9778795SPeter Avalos get_msg(conn, msg); 1714e9778795SPeter Avalos if ((r = sshbuf_get_u8(msg, &type)) != 0 || 1715e9778795SPeter Avalos (r = sshbuf_get_u32(msg, &rid)) != 0) 1716e9778795SPeter Avalos fatal("%s: buffer error: %s", 1717e9778795SPeter Avalos __func__, ssh_err(r)); 171818de8d7fSPeter Avalos 171918de8d7fSPeter Avalos if (type != SSH2_FXP_STATUS) 172018de8d7fSPeter Avalos fatal("Expected SSH2_FXP_STATUS(%d) packet, " 172118de8d7fSPeter Avalos "got %d", SSH2_FXP_STATUS, type); 172218de8d7fSPeter Avalos 1723e9778795SPeter Avalos if ((r = sshbuf_get_u32(msg, &status)) != 0) 1724e9778795SPeter Avalos fatal("%s: buffer error: %s", 1725e9778795SPeter Avalos __func__, ssh_err(r)); 1726e9778795SPeter Avalos debug3("SSH2_FXP_STATUS %u", status); 172718de8d7fSPeter Avalos 172818de8d7fSPeter Avalos /* Find the request in our queue */ 172918de8d7fSPeter Avalos for (ack = TAILQ_FIRST(&acks); 1730e9778795SPeter Avalos ack != NULL && ack->id != rid; 173118de8d7fSPeter Avalos ack = TAILQ_NEXT(ack, tq)) 173218de8d7fSPeter Avalos ; 173318de8d7fSPeter Avalos if (ack == NULL) 1734e9778795SPeter Avalos fatal("Can't find request for ID %u", rid); 173518de8d7fSPeter Avalos TAILQ_REMOVE(&acks, ack, tq); 173618de8d7fSPeter Avalos debug3("In write loop, ack for %u %u bytes at %lld", 173718de8d7fSPeter Avalos ack->id, ack->len, (long long)ack->offset); 173818de8d7fSPeter Avalos ++ackid; 173936e94dc5SPeter Avalos progress_counter += ack->len; 174036e94dc5SPeter Avalos free(ack); 174118de8d7fSPeter Avalos } 174218de8d7fSPeter Avalos offset += len; 174318de8d7fSPeter Avalos if (offset < 0) 174418de8d7fSPeter Avalos fatal("%s: offset < 0", __func__); 174518de8d7fSPeter Avalos } 1746e9778795SPeter Avalos sshbuf_free(msg); 174718de8d7fSPeter Avalos 174818de8d7fSPeter Avalos if (showprogress) 174918de8d7fSPeter Avalos stop_progress_meter(); 175036e94dc5SPeter Avalos free(data); 175118de8d7fSPeter Avalos 175218de8d7fSPeter Avalos if (status != SSH2_FX_OK) { 175318de8d7fSPeter Avalos error("Couldn't write to remote file \"%s\": %s", 175418de8d7fSPeter Avalos remote_path, fx2txt(status)); 1755e9778795SPeter Avalos status = SSH2_FX_FAILURE; 175618de8d7fSPeter Avalos } 175718de8d7fSPeter Avalos 175818de8d7fSPeter Avalos if (close(local_fd) == -1) { 175918de8d7fSPeter Avalos error("Couldn't close local file \"%s\": %s", local_path, 176018de8d7fSPeter Avalos strerror(errno)); 1761e9778795SPeter Avalos status = SSH2_FX_FAILURE; 176218de8d7fSPeter Avalos } 176318de8d7fSPeter Avalos 176418de8d7fSPeter Avalos /* Override umask and utimes if asked */ 176536e94dc5SPeter Avalos if (preserve_flag) 176618de8d7fSPeter Avalos do_fsetstat(conn, handle, handle_len, &a); 176718de8d7fSPeter Avalos 176836e94dc5SPeter Avalos if (fsync_flag) 176936e94dc5SPeter Avalos (void)do_fsync(conn, handle, handle_len); 177036e94dc5SPeter Avalos 1771e9778795SPeter Avalos if (do_close(conn, handle, handle_len) != 0) 1772e9778795SPeter Avalos status = SSH2_FX_FAILURE; 1773e9778795SPeter Avalos 177436e94dc5SPeter Avalos free(handle); 177518de8d7fSPeter Avalos 1776e9778795SPeter Avalos return status == SSH2_FX_OK ? 0 : -1; 177718de8d7fSPeter Avalos } 1778856ea928SPeter Avalos 1779856ea928SPeter Avalos static int 1780e9778795SPeter Avalos upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, 1781e9778795SPeter Avalos int depth, int preserve_flag, int print_flag, int resume, int fsync_flag) 1782856ea928SPeter Avalos { 1783e9778795SPeter Avalos int ret = 0; 1784856ea928SPeter Avalos DIR *dirp; 1785856ea928SPeter Avalos struct dirent *dp; 1786856ea928SPeter Avalos char *filename, *new_src, *new_dst; 1787856ea928SPeter Avalos struct stat sb; 1788e9778795SPeter Avalos Attrib a, *dirattrib; 1789856ea928SPeter Avalos 1790856ea928SPeter Avalos if (depth >= MAX_DIR_DEPTH) { 1791856ea928SPeter Avalos error("Maximum directory depth exceeded: %d levels", depth); 1792856ea928SPeter Avalos return -1; 1793856ea928SPeter Avalos } 1794856ea928SPeter Avalos 1795856ea928SPeter Avalos if (stat(src, &sb) == -1) { 1796856ea928SPeter Avalos error("Couldn't stat directory \"%s\": %s", 1797856ea928SPeter Avalos src, strerror(errno)); 1798856ea928SPeter Avalos return -1; 1799856ea928SPeter Avalos } 1800856ea928SPeter Avalos if (!S_ISDIR(sb.st_mode)) { 1801856ea928SPeter Avalos error("\"%s\" is not a directory", src); 1802856ea928SPeter Avalos return -1; 1803856ea928SPeter Avalos } 180436e94dc5SPeter Avalos if (print_flag) 1805e9778795SPeter Avalos mprintf("Entering %s\n", src); 1806856ea928SPeter Avalos 1807856ea928SPeter Avalos attrib_clear(&a); 1808856ea928SPeter Avalos stat_to_attrib(&sb, &a); 1809856ea928SPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 1810856ea928SPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 1811856ea928SPeter Avalos a.perm &= 01777; 181236e94dc5SPeter Avalos if (!preserve_flag) 1813856ea928SPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1814856ea928SPeter Avalos 1815856ea928SPeter Avalos /* 1816e9778795SPeter Avalos * sftp lacks a portable status value to match errno EEXIST, 1817e9778795SPeter Avalos * so if we get a failure back then we must check whether 1818e9778795SPeter Avalos * the path already existed and is a directory. 1819856ea928SPeter Avalos */ 1820e9778795SPeter Avalos if (do_mkdir(conn, dst, &a, 0) != 0) { 1821e9778795SPeter Avalos if ((dirattrib = do_stat(conn, dst, 0)) == NULL) 1822856ea928SPeter Avalos return -1; 1823e9778795SPeter Avalos if (!S_ISDIR(dirattrib->perm)) { 1824e9778795SPeter Avalos error("\"%s\" exists but is not a directory", dst); 1825856ea928SPeter Avalos return -1; 1826856ea928SPeter Avalos } 1827e9778795SPeter Avalos } 1828856ea928SPeter Avalos 1829856ea928SPeter Avalos if ((dirp = opendir(src)) == NULL) { 1830856ea928SPeter Avalos error("Failed to open dir \"%s\": %s", src, strerror(errno)); 1831856ea928SPeter Avalos return -1; 1832856ea928SPeter Avalos } 1833856ea928SPeter Avalos 1834856ea928SPeter Avalos while (((dp = readdir(dirp)) != NULL) && !interrupted) { 1835856ea928SPeter Avalos if (dp->d_ino == 0) 1836856ea928SPeter Avalos continue; 1837856ea928SPeter Avalos filename = dp->d_name; 1838856ea928SPeter Avalos new_dst = path_append(dst, filename); 1839856ea928SPeter Avalos new_src = path_append(src, filename); 1840856ea928SPeter Avalos 1841856ea928SPeter Avalos if (lstat(new_src, &sb) == -1) { 1842856ea928SPeter Avalos logit("%s: lstat failed: %s", filename, 1843856ea928SPeter Avalos strerror(errno)); 1844856ea928SPeter Avalos ret = -1; 1845856ea928SPeter Avalos } else if (S_ISDIR(sb.st_mode)) { 1846856ea928SPeter Avalos if (strcmp(filename, ".") == 0 || 1847856ea928SPeter Avalos strcmp(filename, "..") == 0) 1848856ea928SPeter Avalos continue; 1849856ea928SPeter Avalos 1850856ea928SPeter Avalos if (upload_dir_internal(conn, new_src, new_dst, 185136e94dc5SPeter Avalos depth + 1, preserve_flag, print_flag, resume, 185236e94dc5SPeter Avalos fsync_flag) == -1) 1853856ea928SPeter Avalos ret = -1; 1854856ea928SPeter Avalos } else if (S_ISREG(sb.st_mode)) { 185536e94dc5SPeter Avalos if (do_upload(conn, new_src, new_dst, 185636e94dc5SPeter Avalos preserve_flag, resume, fsync_flag) == -1) { 1857856ea928SPeter Avalos error("Uploading of file %s to %s failed!", 1858856ea928SPeter Avalos new_src, new_dst); 1859856ea928SPeter Avalos ret = -1; 1860856ea928SPeter Avalos } 1861856ea928SPeter Avalos } else 1862856ea928SPeter Avalos logit("%s: not a regular file\n", filename); 186336e94dc5SPeter Avalos free(new_dst); 186436e94dc5SPeter Avalos free(new_src); 1865856ea928SPeter Avalos } 1866856ea928SPeter Avalos 1867856ea928SPeter Avalos do_setstat(conn, dst, &a); 1868856ea928SPeter Avalos 1869856ea928SPeter Avalos (void) closedir(dirp); 1870856ea928SPeter Avalos return ret; 1871856ea928SPeter Avalos } 1872856ea928SPeter Avalos 1873856ea928SPeter Avalos int 1874e9778795SPeter Avalos upload_dir(struct sftp_conn *conn, const char *src, const char *dst, 1875e9778795SPeter Avalos int preserve_flag, int print_flag, int resume, int fsync_flag) 1876856ea928SPeter Avalos { 1877856ea928SPeter Avalos char *dst_canon; 1878856ea928SPeter Avalos int ret; 1879856ea928SPeter Avalos 1880856ea928SPeter Avalos if ((dst_canon = do_realpath(conn, dst)) == NULL) { 188136e94dc5SPeter Avalos error("Unable to canonicalize path \"%s\"", dst); 1882856ea928SPeter Avalos return -1; 1883856ea928SPeter Avalos } 1884856ea928SPeter Avalos 188536e94dc5SPeter Avalos ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, 188636e94dc5SPeter Avalos print_flag, resume, fsync_flag); 188736e94dc5SPeter Avalos 188836e94dc5SPeter Avalos free(dst_canon); 1889856ea928SPeter Avalos return ret; 1890856ea928SPeter Avalos } 1891856ea928SPeter Avalos 1892856ea928SPeter Avalos char * 1893e9778795SPeter Avalos path_append(const char *p1, const char *p2) 1894856ea928SPeter Avalos { 1895856ea928SPeter Avalos char *ret; 1896856ea928SPeter Avalos size_t len = strlen(p1) + strlen(p2) + 2; 1897856ea928SPeter Avalos 1898856ea928SPeter Avalos ret = xmalloc(len); 1899856ea928SPeter Avalos strlcpy(ret, p1, len); 1900856ea928SPeter Avalos if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') 1901856ea928SPeter Avalos strlcat(ret, "/", len); 1902856ea928SPeter Avalos strlcat(ret, p2, len); 1903856ea928SPeter Avalos 1904856ea928SPeter Avalos return(ret); 1905856ea928SPeter Avalos } 1906856ea928SPeter Avalos 1907