1*99e85e0dSPeter Avalos /* $OpenBSD: sftp-client.c,v 1.97 2012/07/02 12:13:26 dtucker 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 #include <sys/param.h> 2718de8d7fSPeter Avalos #ifdef HAVE_SYS_STATVFS_H 2818de8d7fSPeter Avalos #include <sys/statvfs.h> 2918de8d7fSPeter Avalos #endif 3018de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h" 3118de8d7fSPeter Avalos #ifdef HAVE_SYS_STAT_H 3218de8d7fSPeter Avalos # include <sys/stat.h> 3318de8d7fSPeter Avalos #endif 3418de8d7fSPeter Avalos #ifdef HAVE_SYS_TIME_H 3518de8d7fSPeter Avalos # include <sys/time.h> 3618de8d7fSPeter Avalos #endif 3718de8d7fSPeter Avalos #include <sys/uio.h> 3818de8d7fSPeter Avalos 39856ea928SPeter Avalos #include <dirent.h> 4018de8d7fSPeter Avalos #include <errno.h> 4118de8d7fSPeter Avalos #include <fcntl.h> 4218de8d7fSPeter Avalos #include <signal.h> 4318de8d7fSPeter Avalos #include <stdarg.h> 4418de8d7fSPeter Avalos #include <stdio.h> 4518de8d7fSPeter Avalos #include <string.h> 4618de8d7fSPeter Avalos #include <unistd.h> 4718de8d7fSPeter Avalos 4818de8d7fSPeter Avalos #include "xmalloc.h" 4918de8d7fSPeter Avalos #include "buffer.h" 5018de8d7fSPeter Avalos #include "log.h" 5118de8d7fSPeter Avalos #include "atomicio.h" 5218de8d7fSPeter Avalos #include "progressmeter.h" 5318de8d7fSPeter Avalos #include "misc.h" 5418de8d7fSPeter Avalos 5518de8d7fSPeter Avalos #include "sftp.h" 5618de8d7fSPeter Avalos #include "sftp-common.h" 5718de8d7fSPeter Avalos #include "sftp-client.h" 5818de8d7fSPeter Avalos 5918de8d7fSPeter Avalos extern volatile sig_atomic_t interrupted; 6018de8d7fSPeter Avalos extern int showprogress; 6118de8d7fSPeter Avalos 6218de8d7fSPeter Avalos /* Minimum amount of data to read at a time */ 6318de8d7fSPeter Avalos #define MIN_READ_SIZE 512 6418de8d7fSPeter Avalos 65856ea928SPeter Avalos /* Maximum depth to descend in directory trees */ 66856ea928SPeter Avalos #define MAX_DIR_DEPTH 64 67856ea928SPeter Avalos 6818de8d7fSPeter Avalos struct sftp_conn { 6918de8d7fSPeter Avalos int fd_in; 7018de8d7fSPeter Avalos int fd_out; 7118de8d7fSPeter Avalos u_int transfer_buflen; 7218de8d7fSPeter Avalos u_int num_requests; 7318de8d7fSPeter Avalos u_int version; 7418de8d7fSPeter Avalos u_int msg_id; 7518de8d7fSPeter Avalos #define SFTP_EXT_POSIX_RENAME 0x00000001 7618de8d7fSPeter Avalos #define SFTP_EXT_STATVFS 0x00000002 7718de8d7fSPeter Avalos #define SFTP_EXT_FSTATVFS 0x00000004 789f304aafSPeter Avalos #define SFTP_EXT_HARDLINK 0x00000008 7918de8d7fSPeter Avalos u_int exts; 809f304aafSPeter Avalos u_int64_t limit_kbps; 819f304aafSPeter Avalos struct bwlimit bwlimit_in, bwlimit_out; 8218de8d7fSPeter Avalos }; 8318de8d7fSPeter Avalos 84856ea928SPeter Avalos static char * 859f304aafSPeter Avalos get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len, 869f304aafSPeter Avalos const char *errfmt, ...) __attribute__((format(printf, 4, 5))); 879f304aafSPeter Avalos 889f304aafSPeter Avalos /* ARGSUSED */ 899f304aafSPeter Avalos static int 909f304aafSPeter Avalos sftpio(void *_bwlimit, size_t amount) 919f304aafSPeter Avalos { 929f304aafSPeter Avalos struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit; 939f304aafSPeter Avalos 949f304aafSPeter Avalos bandwidth_limit(bwlimit, amount); 959f304aafSPeter Avalos return 0; 969f304aafSPeter Avalos } 97856ea928SPeter Avalos 9818de8d7fSPeter Avalos static void 999f304aafSPeter Avalos send_msg(struct sftp_conn *conn, Buffer *m) 10018de8d7fSPeter Avalos { 10118de8d7fSPeter Avalos u_char mlen[4]; 10218de8d7fSPeter Avalos struct iovec iov[2]; 10318de8d7fSPeter Avalos 10418de8d7fSPeter Avalos if (buffer_len(m) > SFTP_MAX_MSG_LENGTH) 10518de8d7fSPeter Avalos fatal("Outbound message too long %u", buffer_len(m)); 10618de8d7fSPeter Avalos 10718de8d7fSPeter Avalos /* Send length first */ 10818de8d7fSPeter Avalos put_u32(mlen, buffer_len(m)); 10918de8d7fSPeter Avalos iov[0].iov_base = mlen; 11018de8d7fSPeter Avalos iov[0].iov_len = sizeof(mlen); 11118de8d7fSPeter Avalos iov[1].iov_base = buffer_ptr(m); 11218de8d7fSPeter Avalos iov[1].iov_len = buffer_len(m); 11318de8d7fSPeter Avalos 1149f304aafSPeter Avalos if (atomiciov6(writev, conn->fd_out, iov, 2, 1159f304aafSPeter Avalos conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) != 1169f304aafSPeter Avalos buffer_len(m) + sizeof(mlen)) 11718de8d7fSPeter Avalos fatal("Couldn't send packet: %s", strerror(errno)); 11818de8d7fSPeter Avalos 11918de8d7fSPeter Avalos buffer_clear(m); 12018de8d7fSPeter Avalos } 12118de8d7fSPeter Avalos 12218de8d7fSPeter Avalos static void 1239f304aafSPeter Avalos get_msg(struct sftp_conn *conn, Buffer *m) 12418de8d7fSPeter Avalos { 12518de8d7fSPeter Avalos u_int msg_len; 12618de8d7fSPeter Avalos 12718de8d7fSPeter Avalos buffer_append_space(m, 4); 1289f304aafSPeter Avalos if (atomicio6(read, conn->fd_in, buffer_ptr(m), 4, 1299f304aafSPeter Avalos conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) { 13018de8d7fSPeter Avalos if (errno == EPIPE) 13118de8d7fSPeter Avalos fatal("Connection closed"); 13218de8d7fSPeter Avalos else 13318de8d7fSPeter Avalos fatal("Couldn't read packet: %s", strerror(errno)); 13418de8d7fSPeter Avalos } 13518de8d7fSPeter Avalos 13618de8d7fSPeter Avalos msg_len = buffer_get_int(m); 13718de8d7fSPeter Avalos if (msg_len > SFTP_MAX_MSG_LENGTH) 13818de8d7fSPeter Avalos fatal("Received message too long %u", msg_len); 13918de8d7fSPeter Avalos 14018de8d7fSPeter Avalos buffer_append_space(m, msg_len); 1419f304aafSPeter Avalos if (atomicio6(read, conn->fd_in, buffer_ptr(m), msg_len, 1429f304aafSPeter Avalos conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) 1439f304aafSPeter Avalos != msg_len) { 14418de8d7fSPeter Avalos if (errno == EPIPE) 14518de8d7fSPeter Avalos fatal("Connection closed"); 14618de8d7fSPeter Avalos else 14718de8d7fSPeter Avalos fatal("Read packet: %s", strerror(errno)); 14818de8d7fSPeter Avalos } 14918de8d7fSPeter Avalos } 15018de8d7fSPeter Avalos 15118de8d7fSPeter Avalos static void 1529f304aafSPeter Avalos send_string_request(struct sftp_conn *conn, u_int id, u_int code, char *s, 15318de8d7fSPeter Avalos u_int len) 15418de8d7fSPeter Avalos { 15518de8d7fSPeter Avalos Buffer msg; 15618de8d7fSPeter Avalos 15718de8d7fSPeter Avalos buffer_init(&msg); 15818de8d7fSPeter Avalos buffer_put_char(&msg, code); 15918de8d7fSPeter Avalos buffer_put_int(&msg, id); 16018de8d7fSPeter Avalos buffer_put_string(&msg, s, len); 1619f304aafSPeter Avalos send_msg(conn, &msg); 1629f304aafSPeter Avalos debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); 16318de8d7fSPeter Avalos buffer_free(&msg); 16418de8d7fSPeter Avalos } 16518de8d7fSPeter Avalos 16618de8d7fSPeter Avalos static void 1679f304aafSPeter Avalos send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code, 1689f304aafSPeter Avalos char *s, u_int len, Attrib *a) 16918de8d7fSPeter Avalos { 17018de8d7fSPeter Avalos Buffer msg; 17118de8d7fSPeter Avalos 17218de8d7fSPeter Avalos buffer_init(&msg); 17318de8d7fSPeter Avalos buffer_put_char(&msg, code); 17418de8d7fSPeter Avalos buffer_put_int(&msg, id); 17518de8d7fSPeter Avalos buffer_put_string(&msg, s, len); 17618de8d7fSPeter Avalos encode_attrib(&msg, a); 1779f304aafSPeter Avalos send_msg(conn, &msg); 1789f304aafSPeter Avalos debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); 17918de8d7fSPeter Avalos buffer_free(&msg); 18018de8d7fSPeter Avalos } 18118de8d7fSPeter Avalos 18218de8d7fSPeter Avalos static u_int 1839f304aafSPeter Avalos get_status(struct sftp_conn *conn, u_int expected_id) 18418de8d7fSPeter Avalos { 18518de8d7fSPeter Avalos Buffer msg; 18618de8d7fSPeter Avalos u_int type, id, status; 18718de8d7fSPeter Avalos 18818de8d7fSPeter Avalos buffer_init(&msg); 1899f304aafSPeter Avalos get_msg(conn, &msg); 19018de8d7fSPeter Avalos type = buffer_get_char(&msg); 19118de8d7fSPeter Avalos id = buffer_get_int(&msg); 19218de8d7fSPeter Avalos 19318de8d7fSPeter Avalos if (id != expected_id) 19418de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 19518de8d7fSPeter Avalos if (type != SSH2_FXP_STATUS) 19618de8d7fSPeter Avalos fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", 19718de8d7fSPeter Avalos SSH2_FXP_STATUS, type); 19818de8d7fSPeter Avalos 19918de8d7fSPeter Avalos status = buffer_get_int(&msg); 20018de8d7fSPeter Avalos buffer_free(&msg); 20118de8d7fSPeter Avalos 20218de8d7fSPeter Avalos debug3("SSH2_FXP_STATUS %u", status); 20318de8d7fSPeter Avalos 2049f304aafSPeter Avalos return status; 20518de8d7fSPeter Avalos } 20618de8d7fSPeter Avalos 20718de8d7fSPeter Avalos static char * 2089f304aafSPeter Avalos get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len, 2099f304aafSPeter Avalos const char *errfmt, ...) 21018de8d7fSPeter Avalos { 21118de8d7fSPeter Avalos Buffer msg; 21218de8d7fSPeter Avalos u_int type, id; 213856ea928SPeter Avalos char *handle, errmsg[256]; 214856ea928SPeter Avalos va_list args; 215856ea928SPeter Avalos int status; 216856ea928SPeter Avalos 217856ea928SPeter Avalos va_start(args, errfmt); 218856ea928SPeter Avalos if (errfmt != NULL) 219856ea928SPeter Avalos vsnprintf(errmsg, sizeof(errmsg), errfmt, args); 220856ea928SPeter Avalos va_end(args); 22118de8d7fSPeter Avalos 22218de8d7fSPeter Avalos buffer_init(&msg); 2239f304aafSPeter Avalos get_msg(conn, &msg); 22418de8d7fSPeter Avalos type = buffer_get_char(&msg); 22518de8d7fSPeter Avalos id = buffer_get_int(&msg); 22618de8d7fSPeter Avalos 22718de8d7fSPeter Avalos if (id != expected_id) 228856ea928SPeter Avalos fatal("%s: ID mismatch (%u != %u)", 229856ea928SPeter Avalos errfmt == NULL ? __func__ : errmsg, id, expected_id); 23018de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 231856ea928SPeter Avalos status = buffer_get_int(&msg); 232856ea928SPeter Avalos if (errfmt != NULL) 233856ea928SPeter Avalos error("%s: %s", errmsg, fx2txt(status)); 23418de8d7fSPeter Avalos buffer_free(&msg); 23518de8d7fSPeter Avalos return(NULL); 23618de8d7fSPeter Avalos } else if (type != SSH2_FXP_HANDLE) 237856ea928SPeter Avalos fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", 238856ea928SPeter Avalos errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); 23918de8d7fSPeter Avalos 24018de8d7fSPeter Avalos handle = buffer_get_string(&msg, len); 24118de8d7fSPeter Avalos buffer_free(&msg); 24218de8d7fSPeter Avalos 24318de8d7fSPeter Avalos return(handle); 24418de8d7fSPeter Avalos } 24518de8d7fSPeter Avalos 24618de8d7fSPeter Avalos static Attrib * 2479f304aafSPeter Avalos get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet) 24818de8d7fSPeter Avalos { 24918de8d7fSPeter Avalos Buffer msg; 25018de8d7fSPeter Avalos u_int type, id; 25118de8d7fSPeter Avalos Attrib *a; 25218de8d7fSPeter Avalos 25318de8d7fSPeter Avalos buffer_init(&msg); 2549f304aafSPeter Avalos get_msg(conn, &msg); 25518de8d7fSPeter Avalos 25618de8d7fSPeter Avalos type = buffer_get_char(&msg); 25718de8d7fSPeter Avalos id = buffer_get_int(&msg); 25818de8d7fSPeter Avalos 25918de8d7fSPeter Avalos debug3("Received stat reply T:%u I:%u", type, id); 26018de8d7fSPeter Avalos if (id != expected_id) 26118de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 26218de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 26318de8d7fSPeter Avalos int status = buffer_get_int(&msg); 26418de8d7fSPeter Avalos 26518de8d7fSPeter Avalos if (quiet) 26618de8d7fSPeter Avalos debug("Couldn't stat remote file: %s", fx2txt(status)); 26718de8d7fSPeter Avalos else 26818de8d7fSPeter Avalos error("Couldn't stat remote file: %s", fx2txt(status)); 26918de8d7fSPeter Avalos buffer_free(&msg); 27018de8d7fSPeter Avalos return(NULL); 27118de8d7fSPeter Avalos } else if (type != SSH2_FXP_ATTRS) { 27218de8d7fSPeter Avalos fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", 27318de8d7fSPeter Avalos SSH2_FXP_ATTRS, type); 27418de8d7fSPeter Avalos } 27518de8d7fSPeter Avalos a = decode_attrib(&msg); 27618de8d7fSPeter Avalos buffer_free(&msg); 27718de8d7fSPeter Avalos 27818de8d7fSPeter Avalos return(a); 27918de8d7fSPeter Avalos } 28018de8d7fSPeter Avalos 28118de8d7fSPeter Avalos static int 2829f304aafSPeter Avalos get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st, 2839f304aafSPeter Avalos u_int expected_id, int quiet) 28418de8d7fSPeter Avalos { 28518de8d7fSPeter Avalos Buffer msg; 28618de8d7fSPeter Avalos u_int type, id, flag; 28718de8d7fSPeter Avalos 28818de8d7fSPeter Avalos buffer_init(&msg); 2899f304aafSPeter Avalos get_msg(conn, &msg); 29018de8d7fSPeter Avalos 29118de8d7fSPeter Avalos type = buffer_get_char(&msg); 29218de8d7fSPeter Avalos id = buffer_get_int(&msg); 29318de8d7fSPeter Avalos 29418de8d7fSPeter Avalos debug3("Received statvfs reply T:%u I:%u", type, id); 29518de8d7fSPeter Avalos if (id != expected_id) 29618de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 29718de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 29818de8d7fSPeter Avalos int status = buffer_get_int(&msg); 29918de8d7fSPeter Avalos 30018de8d7fSPeter Avalos if (quiet) 30118de8d7fSPeter Avalos debug("Couldn't statvfs: %s", fx2txt(status)); 30218de8d7fSPeter Avalos else 30318de8d7fSPeter Avalos error("Couldn't statvfs: %s", fx2txt(status)); 30418de8d7fSPeter Avalos buffer_free(&msg); 30518de8d7fSPeter Avalos return -1; 30618de8d7fSPeter Avalos } else if (type != SSH2_FXP_EXTENDED_REPLY) { 30718de8d7fSPeter Avalos fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", 30818de8d7fSPeter Avalos SSH2_FXP_EXTENDED_REPLY, type); 30918de8d7fSPeter Avalos } 31018de8d7fSPeter Avalos 31118de8d7fSPeter Avalos bzero(st, sizeof(*st)); 31218de8d7fSPeter Avalos st->f_bsize = buffer_get_int64(&msg); 31318de8d7fSPeter Avalos st->f_frsize = buffer_get_int64(&msg); 31418de8d7fSPeter Avalos st->f_blocks = buffer_get_int64(&msg); 31518de8d7fSPeter Avalos st->f_bfree = buffer_get_int64(&msg); 31618de8d7fSPeter Avalos st->f_bavail = buffer_get_int64(&msg); 31718de8d7fSPeter Avalos st->f_files = buffer_get_int64(&msg); 31818de8d7fSPeter Avalos st->f_ffree = buffer_get_int64(&msg); 31918de8d7fSPeter Avalos st->f_favail = buffer_get_int64(&msg); 32018de8d7fSPeter Avalos st->f_fsid = buffer_get_int64(&msg); 32118de8d7fSPeter Avalos flag = buffer_get_int64(&msg); 32218de8d7fSPeter Avalos st->f_namemax = buffer_get_int64(&msg); 32318de8d7fSPeter Avalos 32418de8d7fSPeter Avalos st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0; 32518de8d7fSPeter Avalos st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0; 32618de8d7fSPeter Avalos 32718de8d7fSPeter Avalos buffer_free(&msg); 32818de8d7fSPeter Avalos 32918de8d7fSPeter Avalos return 0; 33018de8d7fSPeter Avalos } 33118de8d7fSPeter Avalos 33218de8d7fSPeter Avalos struct sftp_conn * 3339f304aafSPeter Avalos do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, 3349f304aafSPeter Avalos u_int64_t limit_kbps) 33518de8d7fSPeter Avalos { 3369f304aafSPeter Avalos u_int type; 33718de8d7fSPeter Avalos Buffer msg; 33818de8d7fSPeter Avalos struct sftp_conn *ret; 33918de8d7fSPeter Avalos 3409f304aafSPeter Avalos ret = xmalloc(sizeof(*ret)); 3419f304aafSPeter Avalos ret->fd_in = fd_in; 3429f304aafSPeter Avalos ret->fd_out = fd_out; 3439f304aafSPeter Avalos ret->transfer_buflen = transfer_buflen; 3449f304aafSPeter Avalos ret->num_requests = num_requests; 3459f304aafSPeter Avalos ret->exts = 0; 3469f304aafSPeter Avalos ret->limit_kbps = 0; 3479f304aafSPeter Avalos 34818de8d7fSPeter Avalos buffer_init(&msg); 34918de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_INIT); 35018de8d7fSPeter Avalos buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 3519f304aafSPeter Avalos send_msg(ret, &msg); 35218de8d7fSPeter Avalos 35318de8d7fSPeter Avalos buffer_clear(&msg); 35418de8d7fSPeter Avalos 3559f304aafSPeter Avalos get_msg(ret, &msg); 35618de8d7fSPeter Avalos 35718de8d7fSPeter Avalos /* Expecting a VERSION reply */ 35818de8d7fSPeter Avalos if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { 35918de8d7fSPeter Avalos error("Invalid packet back from SSH2_FXP_INIT (type %u)", 36018de8d7fSPeter Avalos type); 36118de8d7fSPeter Avalos buffer_free(&msg); 36218de8d7fSPeter Avalos return(NULL); 36318de8d7fSPeter Avalos } 3649f304aafSPeter Avalos ret->version = buffer_get_int(&msg); 36518de8d7fSPeter Avalos 3669f304aafSPeter Avalos debug2("Remote version: %u", ret->version); 36718de8d7fSPeter Avalos 36818de8d7fSPeter Avalos /* Check for extensions */ 36918de8d7fSPeter Avalos while (buffer_len(&msg) > 0) { 37018de8d7fSPeter Avalos char *name = buffer_get_string(&msg, NULL); 37118de8d7fSPeter Avalos char *value = buffer_get_string(&msg, NULL); 37218de8d7fSPeter Avalos int known = 0; 37318de8d7fSPeter Avalos 37418de8d7fSPeter Avalos if (strcmp(name, "posix-rename@openssh.com") == 0 && 37518de8d7fSPeter Avalos strcmp(value, "1") == 0) { 3769f304aafSPeter Avalos ret->exts |= SFTP_EXT_POSIX_RENAME; 37718de8d7fSPeter Avalos known = 1; 37818de8d7fSPeter Avalos } else if (strcmp(name, "statvfs@openssh.com") == 0 && 37918de8d7fSPeter Avalos strcmp(value, "2") == 0) { 3809f304aafSPeter Avalos ret->exts |= SFTP_EXT_STATVFS; 38118de8d7fSPeter Avalos known = 1; 3829f304aafSPeter Avalos } else if (strcmp(name, "fstatvfs@openssh.com") == 0 && 38318de8d7fSPeter Avalos strcmp(value, "2") == 0) { 3849f304aafSPeter Avalos ret->exts |= SFTP_EXT_FSTATVFS; 3859f304aafSPeter Avalos known = 1; 3869f304aafSPeter Avalos } else if (strcmp(name, "hardlink@openssh.com") == 0 && 3879f304aafSPeter Avalos strcmp(value, "1") == 0) { 3889f304aafSPeter Avalos ret->exts |= SFTP_EXT_HARDLINK; 38918de8d7fSPeter Avalos known = 1; 39018de8d7fSPeter Avalos } 39118de8d7fSPeter Avalos if (known) { 39218de8d7fSPeter Avalos debug2("Server supports extension \"%s\" revision %s", 39318de8d7fSPeter Avalos name, value); 39418de8d7fSPeter Avalos } else { 39518de8d7fSPeter Avalos debug2("Unrecognised server extension \"%s\"", name); 39618de8d7fSPeter Avalos } 39718de8d7fSPeter Avalos xfree(name); 39818de8d7fSPeter Avalos xfree(value); 39918de8d7fSPeter Avalos } 40018de8d7fSPeter Avalos 40118de8d7fSPeter Avalos buffer_free(&msg); 40218de8d7fSPeter Avalos 40318de8d7fSPeter Avalos /* Some filexfer v.0 servers don't support large packets */ 4049f304aafSPeter Avalos if (ret->version == 0) 40518de8d7fSPeter Avalos ret->transfer_buflen = MIN(ret->transfer_buflen, 20480); 40618de8d7fSPeter Avalos 4079f304aafSPeter Avalos ret->limit_kbps = limit_kbps; 4089f304aafSPeter Avalos if (ret->limit_kbps > 0) { 4099f304aafSPeter Avalos bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps, 4109f304aafSPeter Avalos ret->transfer_buflen); 4119f304aafSPeter Avalos bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps, 4129f304aafSPeter Avalos ret->transfer_buflen); 4139f304aafSPeter Avalos } 4149f304aafSPeter Avalos 4159f304aafSPeter Avalos return ret; 41618de8d7fSPeter Avalos } 41718de8d7fSPeter Avalos 41818de8d7fSPeter Avalos u_int 41918de8d7fSPeter Avalos sftp_proto_version(struct sftp_conn *conn) 42018de8d7fSPeter Avalos { 4219f304aafSPeter Avalos return conn->version; 42218de8d7fSPeter Avalos } 42318de8d7fSPeter Avalos 42418de8d7fSPeter Avalos int 42518de8d7fSPeter Avalos do_close(struct sftp_conn *conn, char *handle, u_int handle_len) 42618de8d7fSPeter Avalos { 42718de8d7fSPeter Avalos u_int id, status; 42818de8d7fSPeter Avalos Buffer msg; 42918de8d7fSPeter Avalos 43018de8d7fSPeter Avalos buffer_init(&msg); 43118de8d7fSPeter Avalos 43218de8d7fSPeter Avalos id = conn->msg_id++; 43318de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_CLOSE); 43418de8d7fSPeter Avalos buffer_put_int(&msg, id); 43518de8d7fSPeter Avalos buffer_put_string(&msg, handle, handle_len); 4369f304aafSPeter Avalos send_msg(conn, &msg); 43718de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_CLOSE I:%u", id); 43818de8d7fSPeter Avalos 4399f304aafSPeter Avalos status = get_status(conn, id); 44018de8d7fSPeter Avalos if (status != SSH2_FX_OK) 44118de8d7fSPeter Avalos error("Couldn't close file: %s", fx2txt(status)); 44218de8d7fSPeter Avalos 44318de8d7fSPeter Avalos buffer_free(&msg); 44418de8d7fSPeter Avalos 4459f304aafSPeter Avalos return status; 44618de8d7fSPeter Avalos } 44718de8d7fSPeter Avalos 44818de8d7fSPeter Avalos 44918de8d7fSPeter Avalos static int 45018de8d7fSPeter Avalos do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, 45118de8d7fSPeter Avalos SFTP_DIRENT ***dir) 45218de8d7fSPeter Avalos { 45318de8d7fSPeter Avalos Buffer msg; 45418de8d7fSPeter Avalos u_int count, type, id, handle_len, i, expected_id, ents = 0; 45518de8d7fSPeter Avalos char *handle; 45618de8d7fSPeter Avalos 45718de8d7fSPeter Avalos id = conn->msg_id++; 45818de8d7fSPeter Avalos 45918de8d7fSPeter Avalos buffer_init(&msg); 46018de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_OPENDIR); 46118de8d7fSPeter Avalos buffer_put_int(&msg, id); 46218de8d7fSPeter Avalos buffer_put_cstring(&msg, path); 4639f304aafSPeter Avalos send_msg(conn, &msg); 46418de8d7fSPeter Avalos 4659f304aafSPeter Avalos handle = get_handle(conn, id, &handle_len, 466856ea928SPeter Avalos "remote readdir(\"%s\")", path); 467*99e85e0dSPeter Avalos if (handle == NULL) { 468*99e85e0dSPeter Avalos buffer_free(&msg); 4699f304aafSPeter Avalos return -1; 470*99e85e0dSPeter Avalos } 47118de8d7fSPeter Avalos 47218de8d7fSPeter Avalos if (dir) { 47318de8d7fSPeter Avalos ents = 0; 47418de8d7fSPeter Avalos *dir = xmalloc(sizeof(**dir)); 47518de8d7fSPeter Avalos (*dir)[0] = NULL; 47618de8d7fSPeter Avalos } 47718de8d7fSPeter Avalos 47818de8d7fSPeter Avalos for (; !interrupted;) { 47918de8d7fSPeter Avalos id = expected_id = conn->msg_id++; 48018de8d7fSPeter Avalos 48118de8d7fSPeter Avalos debug3("Sending SSH2_FXP_READDIR I:%u", id); 48218de8d7fSPeter Avalos 48318de8d7fSPeter Avalos buffer_clear(&msg); 48418de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_READDIR); 48518de8d7fSPeter Avalos buffer_put_int(&msg, id); 48618de8d7fSPeter Avalos buffer_put_string(&msg, handle, handle_len); 4879f304aafSPeter Avalos send_msg(conn, &msg); 48818de8d7fSPeter Avalos 48918de8d7fSPeter Avalos buffer_clear(&msg); 49018de8d7fSPeter Avalos 4919f304aafSPeter Avalos get_msg(conn, &msg); 49218de8d7fSPeter Avalos 49318de8d7fSPeter Avalos type = buffer_get_char(&msg); 49418de8d7fSPeter Avalos id = buffer_get_int(&msg); 49518de8d7fSPeter Avalos 49618de8d7fSPeter Avalos debug3("Received reply T:%u I:%u", type, id); 49718de8d7fSPeter Avalos 49818de8d7fSPeter Avalos if (id != expected_id) 49918de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 50018de8d7fSPeter Avalos 50118de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 50218de8d7fSPeter Avalos int status = buffer_get_int(&msg); 50318de8d7fSPeter Avalos 50418de8d7fSPeter Avalos debug3("Received SSH2_FXP_STATUS %d", status); 50518de8d7fSPeter Avalos 50618de8d7fSPeter Avalos if (status == SSH2_FX_EOF) { 50718de8d7fSPeter Avalos break; 50818de8d7fSPeter Avalos } else { 50918de8d7fSPeter Avalos error("Couldn't read directory: %s", 51018de8d7fSPeter Avalos fx2txt(status)); 51118de8d7fSPeter Avalos do_close(conn, handle, handle_len); 51218de8d7fSPeter Avalos xfree(handle); 513*99e85e0dSPeter Avalos buffer_free(&msg); 51418de8d7fSPeter Avalos return(status); 51518de8d7fSPeter Avalos } 51618de8d7fSPeter Avalos } else if (type != SSH2_FXP_NAME) 51718de8d7fSPeter Avalos fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 51818de8d7fSPeter Avalos SSH2_FXP_NAME, type); 51918de8d7fSPeter Avalos 52018de8d7fSPeter Avalos count = buffer_get_int(&msg); 52118de8d7fSPeter Avalos if (count == 0) 52218de8d7fSPeter Avalos break; 52318de8d7fSPeter Avalos debug3("Received %d SSH2_FXP_NAME responses", count); 52418de8d7fSPeter Avalos for (i = 0; i < count; i++) { 52518de8d7fSPeter Avalos char *filename, *longname; 52618de8d7fSPeter Avalos Attrib *a; 52718de8d7fSPeter Avalos 52818de8d7fSPeter Avalos filename = buffer_get_string(&msg, NULL); 52918de8d7fSPeter Avalos longname = buffer_get_string(&msg, NULL); 53018de8d7fSPeter Avalos a = decode_attrib(&msg); 53118de8d7fSPeter Avalos 53218de8d7fSPeter Avalos if (printflag) 53318de8d7fSPeter Avalos printf("%s\n", longname); 53418de8d7fSPeter Avalos 535856ea928SPeter Avalos /* 536856ea928SPeter Avalos * Directory entries should never contain '/' 537856ea928SPeter Avalos * These can be used to attack recursive ops 538856ea928SPeter Avalos * (e.g. send '../../../../etc/passwd') 539856ea928SPeter Avalos */ 540856ea928SPeter Avalos if (strchr(filename, '/') != NULL) { 541856ea928SPeter Avalos error("Server sent suspect path \"%s\" " 542856ea928SPeter Avalos "during readdir of \"%s\"", filename, path); 543856ea928SPeter Avalos goto next; 544856ea928SPeter Avalos } 545856ea928SPeter Avalos 54618de8d7fSPeter Avalos if (dir) { 54718de8d7fSPeter Avalos *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); 54818de8d7fSPeter Avalos (*dir)[ents] = xmalloc(sizeof(***dir)); 54918de8d7fSPeter Avalos (*dir)[ents]->filename = xstrdup(filename); 55018de8d7fSPeter Avalos (*dir)[ents]->longname = xstrdup(longname); 55118de8d7fSPeter Avalos memcpy(&(*dir)[ents]->a, a, sizeof(*a)); 55218de8d7fSPeter Avalos (*dir)[++ents] = NULL; 55318de8d7fSPeter Avalos } 554856ea928SPeter Avalos next: 55518de8d7fSPeter Avalos xfree(filename); 55618de8d7fSPeter Avalos xfree(longname); 55718de8d7fSPeter Avalos } 55818de8d7fSPeter Avalos } 55918de8d7fSPeter Avalos 56018de8d7fSPeter Avalos buffer_free(&msg); 56118de8d7fSPeter Avalos do_close(conn, handle, handle_len); 56218de8d7fSPeter Avalos xfree(handle); 56318de8d7fSPeter Avalos 56418de8d7fSPeter Avalos /* Don't return partial matches on interrupt */ 56518de8d7fSPeter Avalos if (interrupted && dir != NULL && *dir != NULL) { 56618de8d7fSPeter Avalos free_sftp_dirents(*dir); 56718de8d7fSPeter Avalos *dir = xmalloc(sizeof(**dir)); 56818de8d7fSPeter Avalos **dir = NULL; 56918de8d7fSPeter Avalos } 57018de8d7fSPeter Avalos 5719f304aafSPeter Avalos return 0; 57218de8d7fSPeter Avalos } 57318de8d7fSPeter Avalos 57418de8d7fSPeter Avalos int 57518de8d7fSPeter Avalos do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir) 57618de8d7fSPeter Avalos { 57718de8d7fSPeter Avalos return(do_lsreaddir(conn, path, 0, dir)); 57818de8d7fSPeter Avalos } 57918de8d7fSPeter Avalos 58018de8d7fSPeter Avalos void free_sftp_dirents(SFTP_DIRENT **s) 58118de8d7fSPeter Avalos { 58218de8d7fSPeter Avalos int i; 58318de8d7fSPeter Avalos 58418de8d7fSPeter Avalos for (i = 0; s[i]; i++) { 58518de8d7fSPeter Avalos xfree(s[i]->filename); 58618de8d7fSPeter Avalos xfree(s[i]->longname); 58718de8d7fSPeter Avalos xfree(s[i]); 58818de8d7fSPeter Avalos } 58918de8d7fSPeter Avalos xfree(s); 59018de8d7fSPeter Avalos } 59118de8d7fSPeter Avalos 59218de8d7fSPeter Avalos int 59318de8d7fSPeter Avalos do_rm(struct sftp_conn *conn, char *path) 59418de8d7fSPeter Avalos { 59518de8d7fSPeter Avalos u_int status, id; 59618de8d7fSPeter Avalos 59718de8d7fSPeter Avalos debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); 59818de8d7fSPeter Avalos 59918de8d7fSPeter Avalos id = conn->msg_id++; 6009f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path)); 6019f304aafSPeter Avalos status = get_status(conn, id); 60218de8d7fSPeter Avalos if (status != SSH2_FX_OK) 60318de8d7fSPeter Avalos error("Couldn't delete file: %s", fx2txt(status)); 60418de8d7fSPeter Avalos return(status); 60518de8d7fSPeter Avalos } 60618de8d7fSPeter Avalos 60718de8d7fSPeter Avalos int 608856ea928SPeter Avalos do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag) 60918de8d7fSPeter Avalos { 61018de8d7fSPeter Avalos u_int status, id; 61118de8d7fSPeter Avalos 61218de8d7fSPeter Avalos id = conn->msg_id++; 6139f304aafSPeter Avalos send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path, 61418de8d7fSPeter Avalos strlen(path), a); 61518de8d7fSPeter Avalos 6169f304aafSPeter Avalos status = get_status(conn, id); 617856ea928SPeter Avalos if (status != SSH2_FX_OK && printflag) 61818de8d7fSPeter Avalos error("Couldn't create directory: %s", fx2txt(status)); 61918de8d7fSPeter Avalos 62018de8d7fSPeter Avalos return(status); 62118de8d7fSPeter Avalos } 62218de8d7fSPeter Avalos 62318de8d7fSPeter Avalos int 62418de8d7fSPeter Avalos do_rmdir(struct sftp_conn *conn, char *path) 62518de8d7fSPeter Avalos { 62618de8d7fSPeter Avalos u_int status, id; 62718de8d7fSPeter Avalos 62818de8d7fSPeter Avalos id = conn->msg_id++; 6299f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_RMDIR, path, 63018de8d7fSPeter Avalos strlen(path)); 63118de8d7fSPeter Avalos 6329f304aafSPeter Avalos status = get_status(conn, id); 63318de8d7fSPeter Avalos if (status != SSH2_FX_OK) 63418de8d7fSPeter Avalos error("Couldn't remove directory: %s", fx2txt(status)); 63518de8d7fSPeter Avalos 63618de8d7fSPeter Avalos return(status); 63718de8d7fSPeter Avalos } 63818de8d7fSPeter Avalos 63918de8d7fSPeter Avalos Attrib * 64018de8d7fSPeter Avalos do_stat(struct sftp_conn *conn, char *path, int quiet) 64118de8d7fSPeter Avalos { 64218de8d7fSPeter Avalos u_int id; 64318de8d7fSPeter Avalos 64418de8d7fSPeter Avalos id = conn->msg_id++; 64518de8d7fSPeter Avalos 6469f304aafSPeter Avalos send_string_request(conn, id, 64718de8d7fSPeter Avalos conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, 64818de8d7fSPeter Avalos path, strlen(path)); 64918de8d7fSPeter Avalos 6509f304aafSPeter Avalos return(get_decode_stat(conn, id, quiet)); 65118de8d7fSPeter Avalos } 65218de8d7fSPeter Avalos 65318de8d7fSPeter Avalos Attrib * 65418de8d7fSPeter Avalos do_lstat(struct sftp_conn *conn, char *path, int quiet) 65518de8d7fSPeter Avalos { 65618de8d7fSPeter Avalos u_int id; 65718de8d7fSPeter Avalos 65818de8d7fSPeter Avalos if (conn->version == 0) { 65918de8d7fSPeter Avalos if (quiet) 66018de8d7fSPeter Avalos debug("Server version does not support lstat operation"); 66118de8d7fSPeter Avalos else 66218de8d7fSPeter Avalos logit("Server version does not support lstat operation"); 66318de8d7fSPeter Avalos return(do_stat(conn, path, quiet)); 66418de8d7fSPeter Avalos } 66518de8d7fSPeter Avalos 66618de8d7fSPeter Avalos id = conn->msg_id++; 6679f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_LSTAT, path, 66818de8d7fSPeter Avalos strlen(path)); 66918de8d7fSPeter Avalos 6709f304aafSPeter Avalos return(get_decode_stat(conn, id, quiet)); 67118de8d7fSPeter Avalos } 67218de8d7fSPeter Avalos 67318de8d7fSPeter Avalos #ifdef notyet 67418de8d7fSPeter Avalos Attrib * 67518de8d7fSPeter Avalos do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) 67618de8d7fSPeter Avalos { 67718de8d7fSPeter Avalos u_int id; 67818de8d7fSPeter Avalos 67918de8d7fSPeter Avalos id = conn->msg_id++; 6809f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_FSTAT, handle, 68118de8d7fSPeter Avalos handle_len); 68218de8d7fSPeter Avalos 6839f304aafSPeter Avalos return(get_decode_stat(conn, id, quiet)); 68418de8d7fSPeter Avalos } 68518de8d7fSPeter Avalos #endif 68618de8d7fSPeter Avalos 68718de8d7fSPeter Avalos int 68818de8d7fSPeter Avalos do_setstat(struct sftp_conn *conn, char *path, Attrib *a) 68918de8d7fSPeter Avalos { 69018de8d7fSPeter Avalos u_int status, id; 69118de8d7fSPeter Avalos 69218de8d7fSPeter Avalos id = conn->msg_id++; 6939f304aafSPeter Avalos send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path, 69418de8d7fSPeter Avalos strlen(path), a); 69518de8d7fSPeter Avalos 6969f304aafSPeter Avalos status = get_status(conn, id); 69718de8d7fSPeter Avalos if (status != SSH2_FX_OK) 69818de8d7fSPeter Avalos error("Couldn't setstat on \"%s\": %s", path, 69918de8d7fSPeter Avalos fx2txt(status)); 70018de8d7fSPeter Avalos 70118de8d7fSPeter Avalos return(status); 70218de8d7fSPeter Avalos } 70318de8d7fSPeter Avalos 70418de8d7fSPeter Avalos int 70518de8d7fSPeter Avalos do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len, 70618de8d7fSPeter Avalos Attrib *a) 70718de8d7fSPeter Avalos { 70818de8d7fSPeter Avalos u_int status, id; 70918de8d7fSPeter Avalos 71018de8d7fSPeter Avalos id = conn->msg_id++; 7119f304aafSPeter Avalos send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle, 71218de8d7fSPeter Avalos handle_len, a); 71318de8d7fSPeter Avalos 7149f304aafSPeter Avalos status = get_status(conn, id); 71518de8d7fSPeter Avalos if (status != SSH2_FX_OK) 71618de8d7fSPeter Avalos error("Couldn't fsetstat: %s", fx2txt(status)); 71718de8d7fSPeter Avalos 71818de8d7fSPeter Avalos return(status); 71918de8d7fSPeter Avalos } 72018de8d7fSPeter Avalos 72118de8d7fSPeter Avalos char * 72218de8d7fSPeter Avalos do_realpath(struct sftp_conn *conn, char *path) 72318de8d7fSPeter Avalos { 72418de8d7fSPeter Avalos Buffer msg; 72518de8d7fSPeter Avalos u_int type, expected_id, count, id; 72618de8d7fSPeter Avalos char *filename, *longname; 72718de8d7fSPeter Avalos Attrib *a; 72818de8d7fSPeter Avalos 72918de8d7fSPeter Avalos expected_id = id = conn->msg_id++; 7309f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_REALPATH, path, 73118de8d7fSPeter Avalos strlen(path)); 73218de8d7fSPeter Avalos 73318de8d7fSPeter Avalos buffer_init(&msg); 73418de8d7fSPeter Avalos 7359f304aafSPeter Avalos get_msg(conn, &msg); 73618de8d7fSPeter Avalos type = buffer_get_char(&msg); 73718de8d7fSPeter Avalos id = buffer_get_int(&msg); 73818de8d7fSPeter Avalos 73918de8d7fSPeter Avalos if (id != expected_id) 74018de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 74118de8d7fSPeter Avalos 74218de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 74318de8d7fSPeter Avalos u_int status = buffer_get_int(&msg); 74418de8d7fSPeter Avalos 74518de8d7fSPeter Avalos error("Couldn't canonicalise: %s", fx2txt(status)); 746856ea928SPeter Avalos buffer_free(&msg); 747856ea928SPeter Avalos return NULL; 74818de8d7fSPeter Avalos } else if (type != SSH2_FXP_NAME) 74918de8d7fSPeter Avalos fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 75018de8d7fSPeter Avalos SSH2_FXP_NAME, type); 75118de8d7fSPeter Avalos 75218de8d7fSPeter Avalos count = buffer_get_int(&msg); 75318de8d7fSPeter Avalos if (count != 1) 75418de8d7fSPeter Avalos fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); 75518de8d7fSPeter Avalos 75618de8d7fSPeter Avalos filename = buffer_get_string(&msg, NULL); 75718de8d7fSPeter Avalos longname = buffer_get_string(&msg, NULL); 75818de8d7fSPeter Avalos a = decode_attrib(&msg); 75918de8d7fSPeter Avalos 760*99e85e0dSPeter Avalos debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename, 761*99e85e0dSPeter Avalos (unsigned long)a->size); 76218de8d7fSPeter Avalos 76318de8d7fSPeter Avalos xfree(longname); 76418de8d7fSPeter Avalos 76518de8d7fSPeter Avalos buffer_free(&msg); 76618de8d7fSPeter Avalos 76718de8d7fSPeter Avalos return(filename); 76818de8d7fSPeter Avalos } 76918de8d7fSPeter Avalos 77018de8d7fSPeter Avalos int 77118de8d7fSPeter Avalos do_rename(struct sftp_conn *conn, char *oldpath, char *newpath) 77218de8d7fSPeter Avalos { 77318de8d7fSPeter Avalos Buffer msg; 77418de8d7fSPeter Avalos u_int status, id; 77518de8d7fSPeter Avalos 77618de8d7fSPeter Avalos buffer_init(&msg); 77718de8d7fSPeter Avalos 77818de8d7fSPeter Avalos /* Send rename request */ 77918de8d7fSPeter Avalos id = conn->msg_id++; 78018de8d7fSPeter Avalos if ((conn->exts & SFTP_EXT_POSIX_RENAME)) { 78118de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_EXTENDED); 78218de8d7fSPeter Avalos buffer_put_int(&msg, id); 78318de8d7fSPeter Avalos buffer_put_cstring(&msg, "posix-rename@openssh.com"); 78418de8d7fSPeter Avalos } else { 78518de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_RENAME); 78618de8d7fSPeter Avalos buffer_put_int(&msg, id); 78718de8d7fSPeter Avalos } 78818de8d7fSPeter Avalos buffer_put_cstring(&msg, oldpath); 78918de8d7fSPeter Avalos buffer_put_cstring(&msg, newpath); 7909f304aafSPeter Avalos send_msg(conn, &msg); 79118de8d7fSPeter Avalos debug3("Sent message %s \"%s\" -> \"%s\"", 79218de8d7fSPeter Avalos (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" : 79318de8d7fSPeter Avalos "SSH2_FXP_RENAME", oldpath, newpath); 79418de8d7fSPeter Avalos buffer_free(&msg); 79518de8d7fSPeter Avalos 7969f304aafSPeter Avalos status = get_status(conn, id); 79718de8d7fSPeter Avalos if (status != SSH2_FX_OK) 79818de8d7fSPeter Avalos error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, 79918de8d7fSPeter Avalos newpath, fx2txt(status)); 80018de8d7fSPeter Avalos 80118de8d7fSPeter Avalos return(status); 80218de8d7fSPeter Avalos } 80318de8d7fSPeter Avalos 80418de8d7fSPeter Avalos int 8059f304aafSPeter Avalos do_hardlink(struct sftp_conn *conn, char *oldpath, char *newpath) 8069f304aafSPeter Avalos { 8079f304aafSPeter Avalos Buffer msg; 8089f304aafSPeter Avalos u_int status, id; 8099f304aafSPeter Avalos 8109f304aafSPeter Avalos if ((conn->exts & SFTP_EXT_HARDLINK) == 0) { 8119f304aafSPeter Avalos error("Server does not support hardlink@openssh.com extension"); 8129f304aafSPeter Avalos return -1; 8139f304aafSPeter Avalos } 8149f304aafSPeter Avalos 815*99e85e0dSPeter Avalos buffer_init(&msg); 816*99e85e0dSPeter Avalos 817*99e85e0dSPeter Avalos /* Send link request */ 818*99e85e0dSPeter Avalos id = conn->msg_id++; 8199f304aafSPeter Avalos buffer_put_char(&msg, SSH2_FXP_EXTENDED); 8209f304aafSPeter Avalos buffer_put_int(&msg, id); 8219f304aafSPeter Avalos buffer_put_cstring(&msg, "hardlink@openssh.com"); 8229f304aafSPeter Avalos buffer_put_cstring(&msg, oldpath); 8239f304aafSPeter Avalos buffer_put_cstring(&msg, newpath); 8249f304aafSPeter Avalos send_msg(conn, &msg); 8259f304aafSPeter Avalos debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"", 8269f304aafSPeter Avalos oldpath, newpath); 8279f304aafSPeter Avalos buffer_free(&msg); 8289f304aafSPeter Avalos 8299f304aafSPeter Avalos status = get_status(conn, id); 8309f304aafSPeter Avalos if (status != SSH2_FX_OK) 8319f304aafSPeter Avalos error("Couldn't link file \"%s\" to \"%s\": %s", oldpath, 8329f304aafSPeter Avalos newpath, fx2txt(status)); 8339f304aafSPeter Avalos 8349f304aafSPeter Avalos return(status); 8359f304aafSPeter Avalos } 8369f304aafSPeter Avalos 8379f304aafSPeter Avalos int 83818de8d7fSPeter Avalos do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath) 83918de8d7fSPeter Avalos { 84018de8d7fSPeter Avalos Buffer msg; 84118de8d7fSPeter Avalos u_int status, id; 84218de8d7fSPeter Avalos 84318de8d7fSPeter Avalos if (conn->version < 3) { 84418de8d7fSPeter Avalos error("This server does not support the symlink operation"); 84518de8d7fSPeter Avalos return(SSH2_FX_OP_UNSUPPORTED); 84618de8d7fSPeter Avalos } 84718de8d7fSPeter Avalos 84818de8d7fSPeter Avalos buffer_init(&msg); 84918de8d7fSPeter Avalos 85018de8d7fSPeter Avalos /* Send symlink request */ 85118de8d7fSPeter Avalos id = conn->msg_id++; 85218de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_SYMLINK); 85318de8d7fSPeter Avalos buffer_put_int(&msg, id); 85418de8d7fSPeter Avalos buffer_put_cstring(&msg, oldpath); 85518de8d7fSPeter Avalos buffer_put_cstring(&msg, newpath); 8569f304aafSPeter Avalos send_msg(conn, &msg); 85718de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, 85818de8d7fSPeter Avalos newpath); 85918de8d7fSPeter Avalos buffer_free(&msg); 86018de8d7fSPeter Avalos 8619f304aafSPeter Avalos status = get_status(conn, id); 86218de8d7fSPeter Avalos if (status != SSH2_FX_OK) 86318de8d7fSPeter Avalos error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, 86418de8d7fSPeter Avalos newpath, fx2txt(status)); 86518de8d7fSPeter Avalos 86618de8d7fSPeter Avalos return(status); 86718de8d7fSPeter Avalos } 86818de8d7fSPeter Avalos 86918de8d7fSPeter Avalos #ifdef notyet 87018de8d7fSPeter Avalos char * 87118de8d7fSPeter Avalos do_readlink(struct sftp_conn *conn, char *path) 87218de8d7fSPeter Avalos { 87318de8d7fSPeter Avalos Buffer msg; 87418de8d7fSPeter Avalos u_int type, expected_id, count, id; 87518de8d7fSPeter Avalos char *filename, *longname; 87618de8d7fSPeter Avalos Attrib *a; 87718de8d7fSPeter Avalos 87818de8d7fSPeter Avalos expected_id = id = conn->msg_id++; 8799f304aafSPeter Avalos send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path)); 88018de8d7fSPeter Avalos 88118de8d7fSPeter Avalos buffer_init(&msg); 88218de8d7fSPeter Avalos 8839f304aafSPeter Avalos get_msg(conn, &msg); 88418de8d7fSPeter Avalos type = buffer_get_char(&msg); 88518de8d7fSPeter Avalos id = buffer_get_int(&msg); 88618de8d7fSPeter Avalos 88718de8d7fSPeter Avalos if (id != expected_id) 88818de8d7fSPeter Avalos fatal("ID mismatch (%u != %u)", id, expected_id); 88918de8d7fSPeter Avalos 89018de8d7fSPeter Avalos if (type == SSH2_FXP_STATUS) { 89118de8d7fSPeter Avalos u_int status = buffer_get_int(&msg); 89218de8d7fSPeter Avalos 89318de8d7fSPeter Avalos error("Couldn't readlink: %s", fx2txt(status)); 894*99e85e0dSPeter Avalos buffer_free(&msg); 89518de8d7fSPeter Avalos return(NULL); 89618de8d7fSPeter Avalos } else if (type != SSH2_FXP_NAME) 89718de8d7fSPeter Avalos fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 89818de8d7fSPeter Avalos SSH2_FXP_NAME, type); 89918de8d7fSPeter Avalos 90018de8d7fSPeter Avalos count = buffer_get_int(&msg); 90118de8d7fSPeter Avalos if (count != 1) 90218de8d7fSPeter Avalos fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); 90318de8d7fSPeter Avalos 90418de8d7fSPeter Avalos filename = buffer_get_string(&msg, NULL); 90518de8d7fSPeter Avalos longname = buffer_get_string(&msg, NULL); 90618de8d7fSPeter Avalos a = decode_attrib(&msg); 90718de8d7fSPeter Avalos 90818de8d7fSPeter Avalos debug3("SSH_FXP_READLINK %s -> %s", path, filename); 90918de8d7fSPeter Avalos 91018de8d7fSPeter Avalos xfree(longname); 91118de8d7fSPeter Avalos 91218de8d7fSPeter Avalos buffer_free(&msg); 91318de8d7fSPeter Avalos 91418de8d7fSPeter Avalos return(filename); 91518de8d7fSPeter Avalos } 91618de8d7fSPeter Avalos #endif 91718de8d7fSPeter Avalos 91818de8d7fSPeter Avalos int 91918de8d7fSPeter Avalos do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, 92018de8d7fSPeter Avalos int quiet) 92118de8d7fSPeter Avalos { 92218de8d7fSPeter Avalos Buffer msg; 92318de8d7fSPeter Avalos u_int id; 92418de8d7fSPeter Avalos 92518de8d7fSPeter Avalos if ((conn->exts & SFTP_EXT_STATVFS) == 0) { 92618de8d7fSPeter Avalos error("Server does not support statvfs@openssh.com extension"); 92718de8d7fSPeter Avalos return -1; 92818de8d7fSPeter Avalos } 92918de8d7fSPeter Avalos 93018de8d7fSPeter Avalos id = conn->msg_id++; 93118de8d7fSPeter Avalos 93218de8d7fSPeter Avalos buffer_init(&msg); 93318de8d7fSPeter Avalos buffer_clear(&msg); 93418de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_EXTENDED); 93518de8d7fSPeter Avalos buffer_put_int(&msg, id); 93618de8d7fSPeter Avalos buffer_put_cstring(&msg, "statvfs@openssh.com"); 93718de8d7fSPeter Avalos buffer_put_cstring(&msg, path); 9389f304aafSPeter Avalos send_msg(conn, &msg); 93918de8d7fSPeter Avalos buffer_free(&msg); 94018de8d7fSPeter Avalos 9419f304aafSPeter Avalos return get_decode_statvfs(conn, st, id, quiet); 94218de8d7fSPeter Avalos } 94318de8d7fSPeter Avalos 94418de8d7fSPeter Avalos #ifdef notyet 94518de8d7fSPeter Avalos int 94618de8d7fSPeter Avalos do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len, 94718de8d7fSPeter Avalos struct sftp_statvfs *st, int quiet) 94818de8d7fSPeter Avalos { 94918de8d7fSPeter Avalos Buffer msg; 95018de8d7fSPeter Avalos u_int id; 95118de8d7fSPeter Avalos 95218de8d7fSPeter Avalos if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) { 95318de8d7fSPeter Avalos error("Server does not support fstatvfs@openssh.com extension"); 95418de8d7fSPeter Avalos return -1; 95518de8d7fSPeter Avalos } 95618de8d7fSPeter Avalos 95718de8d7fSPeter Avalos id = conn->msg_id++; 95818de8d7fSPeter Avalos 95918de8d7fSPeter Avalos buffer_init(&msg); 96018de8d7fSPeter Avalos buffer_clear(&msg); 96118de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_EXTENDED); 96218de8d7fSPeter Avalos buffer_put_int(&msg, id); 96318de8d7fSPeter Avalos buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 96418de8d7fSPeter Avalos buffer_put_string(&msg, handle, handle_len); 9659f304aafSPeter Avalos send_msg(conn, &msg); 96618de8d7fSPeter Avalos buffer_free(&msg); 96718de8d7fSPeter Avalos 9689f304aafSPeter Avalos return get_decode_statvfs(conn, st, id, quiet); 96918de8d7fSPeter Avalos } 97018de8d7fSPeter Avalos #endif 97118de8d7fSPeter Avalos 97218de8d7fSPeter Avalos static void 9739f304aafSPeter Avalos send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset, 9749f304aafSPeter Avalos u_int len, char *handle, u_int handle_len) 97518de8d7fSPeter Avalos { 97618de8d7fSPeter Avalos Buffer msg; 97718de8d7fSPeter Avalos 97818de8d7fSPeter Avalos buffer_init(&msg); 97918de8d7fSPeter Avalos buffer_clear(&msg); 98018de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_READ); 98118de8d7fSPeter Avalos buffer_put_int(&msg, id); 98218de8d7fSPeter Avalos buffer_put_string(&msg, handle, handle_len); 98318de8d7fSPeter Avalos buffer_put_int64(&msg, offset); 98418de8d7fSPeter Avalos buffer_put_int(&msg, len); 9859f304aafSPeter Avalos send_msg(conn, &msg); 98618de8d7fSPeter Avalos buffer_free(&msg); 98718de8d7fSPeter Avalos } 98818de8d7fSPeter Avalos 98918de8d7fSPeter Avalos int 99018de8d7fSPeter Avalos do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 991856ea928SPeter Avalos Attrib *a, int pflag) 99218de8d7fSPeter Avalos { 993856ea928SPeter Avalos Attrib junk; 99418de8d7fSPeter Avalos Buffer msg; 99518de8d7fSPeter Avalos char *handle; 99618de8d7fSPeter Avalos int local_fd, status = 0, write_error; 99718de8d7fSPeter Avalos int read_error, write_errno; 99818de8d7fSPeter Avalos u_int64_t offset, size; 99918de8d7fSPeter Avalos u_int handle_len, mode, type, id, buflen, num_req, max_req; 100018de8d7fSPeter Avalos off_t progress_counter; 100118de8d7fSPeter Avalos struct request { 100218de8d7fSPeter Avalos u_int id; 100318de8d7fSPeter Avalos u_int len; 100418de8d7fSPeter Avalos u_int64_t offset; 100518de8d7fSPeter Avalos TAILQ_ENTRY(request) tq; 100618de8d7fSPeter Avalos }; 100718de8d7fSPeter Avalos TAILQ_HEAD(reqhead, request) requests; 100818de8d7fSPeter Avalos struct request *req; 100918de8d7fSPeter Avalos 101018de8d7fSPeter Avalos TAILQ_INIT(&requests); 101118de8d7fSPeter Avalos 1012856ea928SPeter Avalos if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) 1013856ea928SPeter Avalos return -1; 101418de8d7fSPeter Avalos 101518de8d7fSPeter Avalos /* Do not preserve set[ug]id here, as we do not preserve ownership */ 101618de8d7fSPeter Avalos if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 101718de8d7fSPeter Avalos mode = a->perm & 0777; 101818de8d7fSPeter Avalos else 101918de8d7fSPeter Avalos mode = 0666; 102018de8d7fSPeter Avalos 102118de8d7fSPeter Avalos if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 102218de8d7fSPeter Avalos (!S_ISREG(a->perm))) { 102318de8d7fSPeter Avalos error("Cannot download non-regular file: %s", remote_path); 102418de8d7fSPeter Avalos return(-1); 102518de8d7fSPeter Avalos } 102618de8d7fSPeter Avalos 102718de8d7fSPeter Avalos if (a->flags & SSH2_FILEXFER_ATTR_SIZE) 102818de8d7fSPeter Avalos size = a->size; 102918de8d7fSPeter Avalos else 103018de8d7fSPeter Avalos size = 0; 103118de8d7fSPeter Avalos 103218de8d7fSPeter Avalos buflen = conn->transfer_buflen; 103318de8d7fSPeter Avalos buffer_init(&msg); 103418de8d7fSPeter Avalos 103518de8d7fSPeter Avalos /* Send open request */ 103618de8d7fSPeter Avalos id = conn->msg_id++; 103718de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_OPEN); 103818de8d7fSPeter Avalos buffer_put_int(&msg, id); 103918de8d7fSPeter Avalos buffer_put_cstring(&msg, remote_path); 104018de8d7fSPeter Avalos buffer_put_int(&msg, SSH2_FXF_READ); 104118de8d7fSPeter Avalos attrib_clear(&junk); /* Send empty attributes */ 104218de8d7fSPeter Avalos encode_attrib(&msg, &junk); 10439f304aafSPeter Avalos send_msg(conn, &msg); 104418de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 104518de8d7fSPeter Avalos 10469f304aafSPeter Avalos handle = get_handle(conn, id, &handle_len, 1047856ea928SPeter Avalos "remote open(\"%s\")", remote_path); 104818de8d7fSPeter Avalos if (handle == NULL) { 104918de8d7fSPeter Avalos buffer_free(&msg); 105018de8d7fSPeter Avalos return(-1); 105118de8d7fSPeter Avalos } 105218de8d7fSPeter Avalos 105318de8d7fSPeter Avalos local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, 105418de8d7fSPeter Avalos mode | S_IWRITE); 105518de8d7fSPeter Avalos if (local_fd == -1) { 105618de8d7fSPeter Avalos error("Couldn't open local file \"%s\" for writing: %s", 105718de8d7fSPeter Avalos local_path, strerror(errno)); 105818de8d7fSPeter Avalos do_close(conn, handle, handle_len); 105918de8d7fSPeter Avalos buffer_free(&msg); 106018de8d7fSPeter Avalos xfree(handle); 106118de8d7fSPeter Avalos return(-1); 106218de8d7fSPeter Avalos } 106318de8d7fSPeter Avalos 106418de8d7fSPeter Avalos /* Read from remote and write to local */ 106518de8d7fSPeter Avalos write_error = read_error = write_errno = num_req = offset = 0; 106618de8d7fSPeter Avalos max_req = 1; 106718de8d7fSPeter Avalos progress_counter = 0; 106818de8d7fSPeter Avalos 106918de8d7fSPeter Avalos if (showprogress && size != 0) 107018de8d7fSPeter Avalos start_progress_meter(remote_path, size, &progress_counter); 107118de8d7fSPeter Avalos 107218de8d7fSPeter Avalos while (num_req > 0 || max_req > 0) { 107318de8d7fSPeter Avalos char *data; 107418de8d7fSPeter Avalos u_int len; 107518de8d7fSPeter Avalos 107618de8d7fSPeter Avalos /* 107718de8d7fSPeter Avalos * Simulate EOF on interrupt: stop sending new requests and 107818de8d7fSPeter Avalos * allow outstanding requests to drain gracefully 107918de8d7fSPeter Avalos */ 108018de8d7fSPeter Avalos if (interrupted) { 108118de8d7fSPeter Avalos if (num_req == 0) /* If we haven't started yet... */ 108218de8d7fSPeter Avalos break; 108318de8d7fSPeter Avalos max_req = 0; 108418de8d7fSPeter Avalos } 108518de8d7fSPeter Avalos 108618de8d7fSPeter Avalos /* Send some more requests */ 108718de8d7fSPeter Avalos while (num_req < max_req) { 108818de8d7fSPeter Avalos debug3("Request range %llu -> %llu (%d/%d)", 108918de8d7fSPeter Avalos (unsigned long long)offset, 109018de8d7fSPeter Avalos (unsigned long long)offset + buflen - 1, 109118de8d7fSPeter Avalos num_req, max_req); 109218de8d7fSPeter Avalos req = xmalloc(sizeof(*req)); 109318de8d7fSPeter Avalos req->id = conn->msg_id++; 109418de8d7fSPeter Avalos req->len = buflen; 109518de8d7fSPeter Avalos req->offset = offset; 109618de8d7fSPeter Avalos offset += buflen; 109718de8d7fSPeter Avalos num_req++; 109818de8d7fSPeter Avalos TAILQ_INSERT_TAIL(&requests, req, tq); 10999f304aafSPeter Avalos send_read_request(conn, req->id, req->offset, 110018de8d7fSPeter Avalos req->len, handle, handle_len); 110118de8d7fSPeter Avalos } 110218de8d7fSPeter Avalos 110318de8d7fSPeter Avalos buffer_clear(&msg); 11049f304aafSPeter Avalos get_msg(conn, &msg); 110518de8d7fSPeter Avalos type = buffer_get_char(&msg); 110618de8d7fSPeter Avalos id = buffer_get_int(&msg); 110718de8d7fSPeter Avalos debug3("Received reply T:%u I:%u R:%d", type, id, max_req); 110818de8d7fSPeter Avalos 110918de8d7fSPeter Avalos /* Find the request in our queue */ 111018de8d7fSPeter Avalos for (req = TAILQ_FIRST(&requests); 111118de8d7fSPeter Avalos req != NULL && req->id != id; 111218de8d7fSPeter Avalos req = TAILQ_NEXT(req, tq)) 111318de8d7fSPeter Avalos ; 111418de8d7fSPeter Avalos if (req == NULL) 111518de8d7fSPeter Avalos fatal("Unexpected reply %u", id); 111618de8d7fSPeter Avalos 111718de8d7fSPeter Avalos switch (type) { 111818de8d7fSPeter Avalos case SSH2_FXP_STATUS: 111918de8d7fSPeter Avalos status = buffer_get_int(&msg); 112018de8d7fSPeter Avalos if (status != SSH2_FX_EOF) 112118de8d7fSPeter Avalos read_error = 1; 112218de8d7fSPeter Avalos max_req = 0; 112318de8d7fSPeter Avalos TAILQ_REMOVE(&requests, req, tq); 112418de8d7fSPeter Avalos xfree(req); 112518de8d7fSPeter Avalos num_req--; 112618de8d7fSPeter Avalos break; 112718de8d7fSPeter Avalos case SSH2_FXP_DATA: 112818de8d7fSPeter Avalos data = buffer_get_string(&msg, &len); 112918de8d7fSPeter Avalos debug3("Received data %llu -> %llu", 113018de8d7fSPeter Avalos (unsigned long long)req->offset, 113118de8d7fSPeter Avalos (unsigned long long)req->offset + len - 1); 113218de8d7fSPeter Avalos if (len > req->len) 113318de8d7fSPeter Avalos fatal("Received more data than asked for " 113418de8d7fSPeter Avalos "%u > %u", len, req->len); 113518de8d7fSPeter Avalos if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || 113618de8d7fSPeter Avalos atomicio(vwrite, local_fd, data, len) != len) && 113718de8d7fSPeter Avalos !write_error) { 113818de8d7fSPeter Avalos write_errno = errno; 113918de8d7fSPeter Avalos write_error = 1; 114018de8d7fSPeter Avalos max_req = 0; 114118de8d7fSPeter Avalos } 114218de8d7fSPeter Avalos progress_counter += len; 114318de8d7fSPeter Avalos xfree(data); 114418de8d7fSPeter Avalos 114518de8d7fSPeter Avalos if (len == req->len) { 114618de8d7fSPeter Avalos TAILQ_REMOVE(&requests, req, tq); 114718de8d7fSPeter Avalos xfree(req); 114818de8d7fSPeter Avalos num_req--; 114918de8d7fSPeter Avalos } else { 115018de8d7fSPeter Avalos /* Resend the request for the missing data */ 115118de8d7fSPeter Avalos debug3("Short data block, re-requesting " 115218de8d7fSPeter Avalos "%llu -> %llu (%2d)", 115318de8d7fSPeter Avalos (unsigned long long)req->offset + len, 115418de8d7fSPeter Avalos (unsigned long long)req->offset + 115518de8d7fSPeter Avalos req->len - 1, num_req); 115618de8d7fSPeter Avalos req->id = conn->msg_id++; 115718de8d7fSPeter Avalos req->len -= len; 115818de8d7fSPeter Avalos req->offset += len; 11599f304aafSPeter Avalos send_read_request(conn, req->id, 116018de8d7fSPeter Avalos req->offset, req->len, handle, handle_len); 116118de8d7fSPeter Avalos /* Reduce the request size */ 116218de8d7fSPeter Avalos if (len < buflen) 116318de8d7fSPeter Avalos buflen = MAX(MIN_READ_SIZE, len); 116418de8d7fSPeter Avalos } 116518de8d7fSPeter Avalos if (max_req > 0) { /* max_req = 0 iff EOF received */ 116618de8d7fSPeter Avalos if (size > 0 && offset > size) { 116718de8d7fSPeter Avalos /* Only one request at a time 116818de8d7fSPeter Avalos * after the expected EOF */ 116918de8d7fSPeter Avalos debug3("Finish at %llu (%2d)", 117018de8d7fSPeter Avalos (unsigned long long)offset, 117118de8d7fSPeter Avalos num_req); 117218de8d7fSPeter Avalos max_req = 1; 117318de8d7fSPeter Avalos } else if (max_req <= conn->num_requests) { 117418de8d7fSPeter Avalos ++max_req; 117518de8d7fSPeter Avalos } 117618de8d7fSPeter Avalos } 117718de8d7fSPeter Avalos break; 117818de8d7fSPeter Avalos default: 117918de8d7fSPeter Avalos fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", 118018de8d7fSPeter Avalos SSH2_FXP_DATA, type); 118118de8d7fSPeter Avalos } 118218de8d7fSPeter Avalos } 118318de8d7fSPeter Avalos 118418de8d7fSPeter Avalos if (showprogress && size) 118518de8d7fSPeter Avalos stop_progress_meter(); 118618de8d7fSPeter Avalos 118718de8d7fSPeter Avalos /* Sanity check */ 118818de8d7fSPeter Avalos if (TAILQ_FIRST(&requests) != NULL) 118918de8d7fSPeter Avalos fatal("Transfer complete, but requests still in queue"); 119018de8d7fSPeter Avalos 119118de8d7fSPeter Avalos if (read_error) { 119218de8d7fSPeter Avalos error("Couldn't read from remote file \"%s\" : %s", 119318de8d7fSPeter Avalos remote_path, fx2txt(status)); 119418de8d7fSPeter Avalos do_close(conn, handle, handle_len); 119518de8d7fSPeter Avalos } else if (write_error) { 119618de8d7fSPeter Avalos error("Couldn't write to \"%s\": %s", local_path, 119718de8d7fSPeter Avalos strerror(write_errno)); 119818de8d7fSPeter Avalos status = -1; 119918de8d7fSPeter Avalos do_close(conn, handle, handle_len); 120018de8d7fSPeter Avalos } else { 120118de8d7fSPeter Avalos status = do_close(conn, handle, handle_len); 120218de8d7fSPeter Avalos 120318de8d7fSPeter Avalos /* Override umask and utimes if asked */ 120418de8d7fSPeter Avalos #ifdef HAVE_FCHMOD 120518de8d7fSPeter Avalos if (pflag && fchmod(local_fd, mode) == -1) 120618de8d7fSPeter Avalos #else 120718de8d7fSPeter Avalos if (pflag && chmod(local_path, mode) == -1) 120818de8d7fSPeter Avalos #endif /* HAVE_FCHMOD */ 120918de8d7fSPeter Avalos error("Couldn't set mode on \"%s\": %s", local_path, 121018de8d7fSPeter Avalos strerror(errno)); 121118de8d7fSPeter Avalos if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { 121218de8d7fSPeter Avalos struct timeval tv[2]; 121318de8d7fSPeter Avalos tv[0].tv_sec = a->atime; 121418de8d7fSPeter Avalos tv[1].tv_sec = a->mtime; 121518de8d7fSPeter Avalos tv[0].tv_usec = tv[1].tv_usec = 0; 121618de8d7fSPeter Avalos if (utimes(local_path, tv) == -1) 121718de8d7fSPeter Avalos error("Can't set times on \"%s\": %s", 121818de8d7fSPeter Avalos local_path, strerror(errno)); 121918de8d7fSPeter Avalos } 122018de8d7fSPeter Avalos } 122118de8d7fSPeter Avalos close(local_fd); 122218de8d7fSPeter Avalos buffer_free(&msg); 122318de8d7fSPeter Avalos xfree(handle); 122418de8d7fSPeter Avalos 122518de8d7fSPeter Avalos return(status); 122618de8d7fSPeter Avalos } 122718de8d7fSPeter Avalos 1228856ea928SPeter Avalos static int 1229856ea928SPeter Avalos download_dir_internal(struct sftp_conn *conn, char *src, char *dst, 1230856ea928SPeter Avalos Attrib *dirattrib, int pflag, int printflag, int depth) 1231856ea928SPeter Avalos { 1232856ea928SPeter Avalos int i, ret = 0; 1233856ea928SPeter Avalos SFTP_DIRENT **dir_entries; 1234856ea928SPeter Avalos char *filename, *new_src, *new_dst; 1235856ea928SPeter Avalos mode_t mode = 0777; 1236856ea928SPeter Avalos 1237856ea928SPeter Avalos if (depth >= MAX_DIR_DEPTH) { 1238856ea928SPeter Avalos error("Maximum directory depth exceeded: %d levels", depth); 1239856ea928SPeter Avalos return -1; 1240856ea928SPeter Avalos } 1241856ea928SPeter Avalos 1242856ea928SPeter Avalos if (dirattrib == NULL && 1243856ea928SPeter Avalos (dirattrib = do_stat(conn, src, 1)) == NULL) { 1244856ea928SPeter Avalos error("Unable to stat remote directory \"%s\"", src); 1245856ea928SPeter Avalos return -1; 1246856ea928SPeter Avalos } 1247856ea928SPeter Avalos if (!S_ISDIR(dirattrib->perm)) { 1248856ea928SPeter Avalos error("\"%s\" is not a directory", src); 1249856ea928SPeter Avalos return -1; 1250856ea928SPeter Avalos } 1251856ea928SPeter Avalos if (printflag) 1252856ea928SPeter Avalos printf("Retrieving %s\n", src); 1253856ea928SPeter Avalos 1254856ea928SPeter Avalos if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 1255856ea928SPeter Avalos mode = dirattrib->perm & 01777; 1256856ea928SPeter Avalos else { 1257856ea928SPeter Avalos debug("Server did not send permissions for " 1258856ea928SPeter Avalos "directory \"%s\"", dst); 1259856ea928SPeter Avalos } 1260856ea928SPeter Avalos 1261856ea928SPeter Avalos if (mkdir(dst, mode) == -1 && errno != EEXIST) { 1262856ea928SPeter Avalos error("mkdir %s: %s", dst, strerror(errno)); 1263856ea928SPeter Avalos return -1; 1264856ea928SPeter Avalos } 1265856ea928SPeter Avalos 1266856ea928SPeter Avalos if (do_readdir(conn, src, &dir_entries) == -1) { 1267856ea928SPeter Avalos error("%s: Failed to get directory contents", src); 1268856ea928SPeter Avalos return -1; 1269856ea928SPeter Avalos } 1270856ea928SPeter Avalos 1271856ea928SPeter Avalos for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { 1272856ea928SPeter Avalos filename = dir_entries[i]->filename; 1273856ea928SPeter Avalos 1274856ea928SPeter Avalos new_dst = path_append(dst, filename); 1275856ea928SPeter Avalos new_src = path_append(src, filename); 1276856ea928SPeter Avalos 1277856ea928SPeter Avalos if (S_ISDIR(dir_entries[i]->a.perm)) { 1278856ea928SPeter Avalos if (strcmp(filename, ".") == 0 || 1279856ea928SPeter Avalos strcmp(filename, "..") == 0) 1280856ea928SPeter Avalos continue; 1281856ea928SPeter Avalos if (download_dir_internal(conn, new_src, new_dst, 1282856ea928SPeter Avalos &(dir_entries[i]->a), pflag, printflag, 1283856ea928SPeter Avalos depth + 1) == -1) 1284856ea928SPeter Avalos ret = -1; 1285856ea928SPeter Avalos } else if (S_ISREG(dir_entries[i]->a.perm) ) { 1286856ea928SPeter Avalos if (do_download(conn, new_src, new_dst, 1287856ea928SPeter Avalos &(dir_entries[i]->a), pflag) == -1) { 1288856ea928SPeter Avalos error("Download of file %s to %s failed", 1289856ea928SPeter Avalos new_src, new_dst); 1290856ea928SPeter Avalos ret = -1; 1291856ea928SPeter Avalos } 1292856ea928SPeter Avalos } else 1293856ea928SPeter Avalos logit("%s: not a regular file\n", new_src); 1294856ea928SPeter Avalos 1295856ea928SPeter Avalos xfree(new_dst); 1296856ea928SPeter Avalos xfree(new_src); 1297856ea928SPeter Avalos } 1298856ea928SPeter Avalos 1299856ea928SPeter Avalos if (pflag) { 1300856ea928SPeter Avalos if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 1301856ea928SPeter Avalos struct timeval tv[2]; 1302856ea928SPeter Avalos tv[0].tv_sec = dirattrib->atime; 1303856ea928SPeter Avalos tv[1].tv_sec = dirattrib->mtime; 1304856ea928SPeter Avalos tv[0].tv_usec = tv[1].tv_usec = 0; 1305856ea928SPeter Avalos if (utimes(dst, tv) == -1) 1306856ea928SPeter Avalos error("Can't set times on \"%s\": %s", 1307856ea928SPeter Avalos dst, strerror(errno)); 1308856ea928SPeter Avalos } else 1309856ea928SPeter Avalos debug("Server did not send times for directory " 1310856ea928SPeter Avalos "\"%s\"", dst); 1311856ea928SPeter Avalos } 1312856ea928SPeter Avalos 1313856ea928SPeter Avalos free_sftp_dirents(dir_entries); 1314856ea928SPeter Avalos 1315856ea928SPeter Avalos return ret; 1316856ea928SPeter Avalos } 1317856ea928SPeter Avalos 1318856ea928SPeter Avalos int 1319856ea928SPeter Avalos download_dir(struct sftp_conn *conn, char *src, char *dst, 1320856ea928SPeter Avalos Attrib *dirattrib, int pflag, int printflag) 1321856ea928SPeter Avalos { 1322856ea928SPeter Avalos char *src_canon; 1323856ea928SPeter Avalos int ret; 1324856ea928SPeter Avalos 1325856ea928SPeter Avalos if ((src_canon = do_realpath(conn, src)) == NULL) { 1326856ea928SPeter Avalos error("Unable to canonicalise path \"%s\"", src); 1327856ea928SPeter Avalos return -1; 1328856ea928SPeter Avalos } 1329856ea928SPeter Avalos 1330856ea928SPeter Avalos ret = download_dir_internal(conn, src_canon, dst, 1331856ea928SPeter Avalos dirattrib, pflag, printflag, 0); 1332856ea928SPeter Avalos xfree(src_canon); 1333856ea928SPeter Avalos return ret; 1334856ea928SPeter Avalos } 1335856ea928SPeter Avalos 133618de8d7fSPeter Avalos int 133718de8d7fSPeter Avalos do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 133818de8d7fSPeter Avalos int pflag) 133918de8d7fSPeter Avalos { 134018de8d7fSPeter Avalos int local_fd; 134118de8d7fSPeter Avalos int status = SSH2_FX_OK; 134218de8d7fSPeter Avalos u_int handle_len, id, type; 134318de8d7fSPeter Avalos off_t offset; 134418de8d7fSPeter Avalos char *handle, *data; 134518de8d7fSPeter Avalos Buffer msg; 134618de8d7fSPeter Avalos struct stat sb; 134718de8d7fSPeter Avalos Attrib a; 134818de8d7fSPeter Avalos u_int32_t startid; 134918de8d7fSPeter Avalos u_int32_t ackid; 135018de8d7fSPeter Avalos struct outstanding_ack { 135118de8d7fSPeter Avalos u_int id; 135218de8d7fSPeter Avalos u_int len; 135318de8d7fSPeter Avalos off_t offset; 135418de8d7fSPeter Avalos TAILQ_ENTRY(outstanding_ack) tq; 135518de8d7fSPeter Avalos }; 135618de8d7fSPeter Avalos TAILQ_HEAD(ackhead, outstanding_ack) acks; 135718de8d7fSPeter Avalos struct outstanding_ack *ack = NULL; 135818de8d7fSPeter Avalos 135918de8d7fSPeter Avalos TAILQ_INIT(&acks); 136018de8d7fSPeter Avalos 136118de8d7fSPeter Avalos if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { 136218de8d7fSPeter Avalos error("Couldn't open local file \"%s\" for reading: %s", 136318de8d7fSPeter Avalos local_path, strerror(errno)); 136418de8d7fSPeter Avalos return(-1); 136518de8d7fSPeter Avalos } 136618de8d7fSPeter Avalos if (fstat(local_fd, &sb) == -1) { 136718de8d7fSPeter Avalos error("Couldn't fstat local file \"%s\": %s", 136818de8d7fSPeter Avalos local_path, strerror(errno)); 136918de8d7fSPeter Avalos close(local_fd); 137018de8d7fSPeter Avalos return(-1); 137118de8d7fSPeter Avalos } 137218de8d7fSPeter Avalos if (!S_ISREG(sb.st_mode)) { 137318de8d7fSPeter Avalos error("%s is not a regular file", local_path); 137418de8d7fSPeter Avalos close(local_fd); 137518de8d7fSPeter Avalos return(-1); 137618de8d7fSPeter Avalos } 137718de8d7fSPeter Avalos stat_to_attrib(&sb, &a); 137818de8d7fSPeter Avalos 137918de8d7fSPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 138018de8d7fSPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 138118de8d7fSPeter Avalos a.perm &= 0777; 138218de8d7fSPeter Avalos if (!pflag) 138318de8d7fSPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 138418de8d7fSPeter Avalos 138518de8d7fSPeter Avalos buffer_init(&msg); 138618de8d7fSPeter Avalos 138718de8d7fSPeter Avalos /* Send open request */ 138818de8d7fSPeter Avalos id = conn->msg_id++; 138918de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_OPEN); 139018de8d7fSPeter Avalos buffer_put_int(&msg, id); 139118de8d7fSPeter Avalos buffer_put_cstring(&msg, remote_path); 139218de8d7fSPeter Avalos buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); 139318de8d7fSPeter Avalos encode_attrib(&msg, &a); 13949f304aafSPeter Avalos send_msg(conn, &msg); 139518de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 139618de8d7fSPeter Avalos 139718de8d7fSPeter Avalos buffer_clear(&msg); 139818de8d7fSPeter Avalos 13999f304aafSPeter Avalos handle = get_handle(conn, id, &handle_len, 1400856ea928SPeter Avalos "remote open(\"%s\")", remote_path); 140118de8d7fSPeter Avalos if (handle == NULL) { 140218de8d7fSPeter Avalos close(local_fd); 140318de8d7fSPeter Avalos buffer_free(&msg); 140418de8d7fSPeter Avalos return -1; 140518de8d7fSPeter Avalos } 140618de8d7fSPeter Avalos 140718de8d7fSPeter Avalos startid = ackid = id + 1; 140818de8d7fSPeter Avalos data = xmalloc(conn->transfer_buflen); 140918de8d7fSPeter Avalos 141018de8d7fSPeter Avalos /* Read from local and write to remote */ 141118de8d7fSPeter Avalos offset = 0; 141218de8d7fSPeter Avalos if (showprogress) 141318de8d7fSPeter Avalos start_progress_meter(local_path, sb.st_size, &offset); 141418de8d7fSPeter Avalos 141518de8d7fSPeter Avalos for (;;) { 141618de8d7fSPeter Avalos int len; 141718de8d7fSPeter Avalos 141818de8d7fSPeter Avalos /* 141918de8d7fSPeter Avalos * Can't use atomicio here because it returns 0 on EOF, 142018de8d7fSPeter Avalos * thus losing the last block of the file. 142118de8d7fSPeter Avalos * Simulate an EOF on interrupt, allowing ACKs from the 142218de8d7fSPeter Avalos * server to drain. 142318de8d7fSPeter Avalos */ 142418de8d7fSPeter Avalos if (interrupted || status != SSH2_FX_OK) 142518de8d7fSPeter Avalos len = 0; 142618de8d7fSPeter Avalos else do 142718de8d7fSPeter Avalos len = read(local_fd, data, conn->transfer_buflen); 142818de8d7fSPeter Avalos while ((len == -1) && 142918de8d7fSPeter Avalos (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); 143018de8d7fSPeter Avalos 143118de8d7fSPeter Avalos if (len == -1) 143218de8d7fSPeter Avalos fatal("Couldn't read from \"%s\": %s", local_path, 143318de8d7fSPeter Avalos strerror(errno)); 143418de8d7fSPeter Avalos 143518de8d7fSPeter Avalos if (len != 0) { 143618de8d7fSPeter Avalos ack = xmalloc(sizeof(*ack)); 143718de8d7fSPeter Avalos ack->id = ++id; 143818de8d7fSPeter Avalos ack->offset = offset; 143918de8d7fSPeter Avalos ack->len = len; 144018de8d7fSPeter Avalos TAILQ_INSERT_TAIL(&acks, ack, tq); 144118de8d7fSPeter Avalos 144218de8d7fSPeter Avalos buffer_clear(&msg); 144318de8d7fSPeter Avalos buffer_put_char(&msg, SSH2_FXP_WRITE); 144418de8d7fSPeter Avalos buffer_put_int(&msg, ack->id); 144518de8d7fSPeter Avalos buffer_put_string(&msg, handle, handle_len); 144618de8d7fSPeter Avalos buffer_put_int64(&msg, offset); 144718de8d7fSPeter Avalos buffer_put_string(&msg, data, len); 14489f304aafSPeter Avalos send_msg(conn, &msg); 144918de8d7fSPeter Avalos debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", 145018de8d7fSPeter Avalos id, (unsigned long long)offset, len); 145118de8d7fSPeter Avalos } else if (TAILQ_FIRST(&acks) == NULL) 145218de8d7fSPeter Avalos break; 145318de8d7fSPeter Avalos 145418de8d7fSPeter Avalos if (ack == NULL) 145518de8d7fSPeter Avalos fatal("Unexpected ACK %u", id); 145618de8d7fSPeter Avalos 145718de8d7fSPeter Avalos if (id == startid || len == 0 || 145818de8d7fSPeter Avalos id - ackid >= conn->num_requests) { 145918de8d7fSPeter Avalos u_int r_id; 146018de8d7fSPeter Avalos 146118de8d7fSPeter Avalos buffer_clear(&msg); 14629f304aafSPeter Avalos get_msg(conn, &msg); 146318de8d7fSPeter Avalos type = buffer_get_char(&msg); 146418de8d7fSPeter Avalos r_id = buffer_get_int(&msg); 146518de8d7fSPeter Avalos 146618de8d7fSPeter Avalos if (type != SSH2_FXP_STATUS) 146718de8d7fSPeter Avalos fatal("Expected SSH2_FXP_STATUS(%d) packet, " 146818de8d7fSPeter Avalos "got %d", SSH2_FXP_STATUS, type); 146918de8d7fSPeter Avalos 147018de8d7fSPeter Avalos status = buffer_get_int(&msg); 147118de8d7fSPeter Avalos debug3("SSH2_FXP_STATUS %d", status); 147218de8d7fSPeter Avalos 147318de8d7fSPeter Avalos /* Find the request in our queue */ 147418de8d7fSPeter Avalos for (ack = TAILQ_FIRST(&acks); 147518de8d7fSPeter Avalos ack != NULL && ack->id != r_id; 147618de8d7fSPeter Avalos ack = TAILQ_NEXT(ack, tq)) 147718de8d7fSPeter Avalos ; 147818de8d7fSPeter Avalos if (ack == NULL) 147918de8d7fSPeter Avalos fatal("Can't find request for ID %u", r_id); 148018de8d7fSPeter Avalos TAILQ_REMOVE(&acks, ack, tq); 148118de8d7fSPeter Avalos debug3("In write loop, ack for %u %u bytes at %lld", 148218de8d7fSPeter Avalos ack->id, ack->len, (long long)ack->offset); 148318de8d7fSPeter Avalos ++ackid; 148418de8d7fSPeter Avalos xfree(ack); 148518de8d7fSPeter Avalos } 148618de8d7fSPeter Avalos offset += len; 148718de8d7fSPeter Avalos if (offset < 0) 148818de8d7fSPeter Avalos fatal("%s: offset < 0", __func__); 148918de8d7fSPeter Avalos } 149018de8d7fSPeter Avalos buffer_free(&msg); 149118de8d7fSPeter Avalos 149218de8d7fSPeter Avalos if (showprogress) 149318de8d7fSPeter Avalos stop_progress_meter(); 149418de8d7fSPeter Avalos xfree(data); 149518de8d7fSPeter Avalos 149618de8d7fSPeter Avalos if (status != SSH2_FX_OK) { 149718de8d7fSPeter Avalos error("Couldn't write to remote file \"%s\": %s", 149818de8d7fSPeter Avalos remote_path, fx2txt(status)); 149918de8d7fSPeter Avalos status = -1; 150018de8d7fSPeter Avalos } 150118de8d7fSPeter Avalos 150218de8d7fSPeter Avalos if (close(local_fd) == -1) { 150318de8d7fSPeter Avalos error("Couldn't close local file \"%s\": %s", local_path, 150418de8d7fSPeter Avalos strerror(errno)); 150518de8d7fSPeter Avalos status = -1; 150618de8d7fSPeter Avalos } 150718de8d7fSPeter Avalos 150818de8d7fSPeter Avalos /* Override umask and utimes if asked */ 150918de8d7fSPeter Avalos if (pflag) 151018de8d7fSPeter Avalos do_fsetstat(conn, handle, handle_len, &a); 151118de8d7fSPeter Avalos 151218de8d7fSPeter Avalos if (do_close(conn, handle, handle_len) != SSH2_FX_OK) 151318de8d7fSPeter Avalos status = -1; 151418de8d7fSPeter Avalos xfree(handle); 151518de8d7fSPeter Avalos 151618de8d7fSPeter Avalos return status; 151718de8d7fSPeter Avalos } 1518856ea928SPeter Avalos 1519856ea928SPeter Avalos static int 1520856ea928SPeter Avalos upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, 1521856ea928SPeter Avalos int pflag, int printflag, int depth) 1522856ea928SPeter Avalos { 1523856ea928SPeter Avalos int ret = 0, status; 1524856ea928SPeter Avalos DIR *dirp; 1525856ea928SPeter Avalos struct dirent *dp; 1526856ea928SPeter Avalos char *filename, *new_src, *new_dst; 1527856ea928SPeter Avalos struct stat sb; 1528856ea928SPeter Avalos Attrib a; 1529856ea928SPeter Avalos 1530856ea928SPeter Avalos if (depth >= MAX_DIR_DEPTH) { 1531856ea928SPeter Avalos error("Maximum directory depth exceeded: %d levels", depth); 1532856ea928SPeter Avalos return -1; 1533856ea928SPeter Avalos } 1534856ea928SPeter Avalos 1535856ea928SPeter Avalos if (stat(src, &sb) == -1) { 1536856ea928SPeter Avalos error("Couldn't stat directory \"%s\": %s", 1537856ea928SPeter Avalos src, strerror(errno)); 1538856ea928SPeter Avalos return -1; 1539856ea928SPeter Avalos } 1540856ea928SPeter Avalos if (!S_ISDIR(sb.st_mode)) { 1541856ea928SPeter Avalos error("\"%s\" is not a directory", src); 1542856ea928SPeter Avalos return -1; 1543856ea928SPeter Avalos } 1544856ea928SPeter Avalos if (printflag) 1545856ea928SPeter Avalos printf("Entering %s\n", src); 1546856ea928SPeter Avalos 1547856ea928SPeter Avalos attrib_clear(&a); 1548856ea928SPeter Avalos stat_to_attrib(&sb, &a); 1549856ea928SPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 1550856ea928SPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 1551856ea928SPeter Avalos a.perm &= 01777; 1552856ea928SPeter Avalos if (!pflag) 1553856ea928SPeter Avalos a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1554856ea928SPeter Avalos 1555856ea928SPeter Avalos status = do_mkdir(conn, dst, &a, 0); 1556856ea928SPeter Avalos /* 1557856ea928SPeter Avalos * we lack a portable status for errno EEXIST, 1558856ea928SPeter Avalos * so if we get a SSH2_FX_FAILURE back we must check 1559856ea928SPeter Avalos * if it was created successfully. 1560856ea928SPeter Avalos */ 1561856ea928SPeter Avalos if (status != SSH2_FX_OK) { 1562856ea928SPeter Avalos if (status != SSH2_FX_FAILURE) 1563856ea928SPeter Avalos return -1; 1564856ea928SPeter Avalos if (do_stat(conn, dst, 0) == NULL) 1565856ea928SPeter Avalos return -1; 1566856ea928SPeter Avalos } 1567856ea928SPeter Avalos 1568856ea928SPeter Avalos if ((dirp = opendir(src)) == NULL) { 1569856ea928SPeter Avalos error("Failed to open dir \"%s\": %s", src, strerror(errno)); 1570856ea928SPeter Avalos return -1; 1571856ea928SPeter Avalos } 1572856ea928SPeter Avalos 1573856ea928SPeter Avalos while (((dp = readdir(dirp)) != NULL) && !interrupted) { 1574856ea928SPeter Avalos if (dp->d_ino == 0) 1575856ea928SPeter Avalos continue; 1576856ea928SPeter Avalos filename = dp->d_name; 1577856ea928SPeter Avalos new_dst = path_append(dst, filename); 1578856ea928SPeter Avalos new_src = path_append(src, filename); 1579856ea928SPeter Avalos 1580856ea928SPeter Avalos if (lstat(new_src, &sb) == -1) { 1581856ea928SPeter Avalos logit("%s: lstat failed: %s", filename, 1582856ea928SPeter Avalos strerror(errno)); 1583856ea928SPeter Avalos ret = -1; 1584856ea928SPeter Avalos } else if (S_ISDIR(sb.st_mode)) { 1585856ea928SPeter Avalos if (strcmp(filename, ".") == 0 || 1586856ea928SPeter Avalos strcmp(filename, "..") == 0) 1587856ea928SPeter Avalos continue; 1588856ea928SPeter Avalos 1589856ea928SPeter Avalos if (upload_dir_internal(conn, new_src, new_dst, 1590856ea928SPeter Avalos pflag, printflag, depth + 1) == -1) 1591856ea928SPeter Avalos ret = -1; 1592856ea928SPeter Avalos } else if (S_ISREG(sb.st_mode)) { 1593856ea928SPeter Avalos if (do_upload(conn, new_src, new_dst, pflag) == -1) { 1594856ea928SPeter Avalos error("Uploading of file %s to %s failed!", 1595856ea928SPeter Avalos new_src, new_dst); 1596856ea928SPeter Avalos ret = -1; 1597856ea928SPeter Avalos } 1598856ea928SPeter Avalos } else 1599856ea928SPeter Avalos logit("%s: not a regular file\n", filename); 1600856ea928SPeter Avalos xfree(new_dst); 1601856ea928SPeter Avalos xfree(new_src); 1602856ea928SPeter Avalos } 1603856ea928SPeter Avalos 1604856ea928SPeter Avalos do_setstat(conn, dst, &a); 1605856ea928SPeter Avalos 1606856ea928SPeter Avalos (void) closedir(dirp); 1607856ea928SPeter Avalos return ret; 1608856ea928SPeter Avalos } 1609856ea928SPeter Avalos 1610856ea928SPeter Avalos int 1611856ea928SPeter Avalos upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag, 1612856ea928SPeter Avalos int pflag) 1613856ea928SPeter Avalos { 1614856ea928SPeter Avalos char *dst_canon; 1615856ea928SPeter Avalos int ret; 1616856ea928SPeter Avalos 1617856ea928SPeter Avalos if ((dst_canon = do_realpath(conn, dst)) == NULL) { 1618856ea928SPeter Avalos error("Unable to canonicalise path \"%s\"", dst); 1619856ea928SPeter Avalos return -1; 1620856ea928SPeter Avalos } 1621856ea928SPeter Avalos 1622856ea928SPeter Avalos ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0); 1623856ea928SPeter Avalos xfree(dst_canon); 1624856ea928SPeter Avalos return ret; 1625856ea928SPeter Avalos } 1626856ea928SPeter Avalos 1627856ea928SPeter Avalos char * 1628856ea928SPeter Avalos path_append(char *p1, char *p2) 1629856ea928SPeter Avalos { 1630856ea928SPeter Avalos char *ret; 1631856ea928SPeter Avalos size_t len = strlen(p1) + strlen(p2) + 2; 1632856ea928SPeter Avalos 1633856ea928SPeter Avalos ret = xmalloc(len); 1634856ea928SPeter Avalos strlcpy(ret, p1, len); 1635856ea928SPeter Avalos if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') 1636856ea928SPeter Avalos strlcat(ret, "/", len); 1637856ea928SPeter Avalos strlcat(ret, p2, len); 1638856ea928SPeter Avalos 1639856ea928SPeter Avalos return(ret); 1640856ea928SPeter Avalos } 1641856ea928SPeter Avalos 1642