summaryrefslogtreecommitdiff
path: root/sftp-client.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2013-07-25 11:56:52 +1000
committerDamien Miller <djm@mindrot.org>2013-07-25 11:56:52 +1000
commit0d032419ee6e1968fc1cb187af63bf3b77b506ea (patch)
treece2788365040e9ea188bd60c8bec87d410814017 /sftp-client.c
parent98e27dcf581647b5bbe9780e8f59685d942d8ea3 (diff)
- djm@cvs.openbsd.org 2013/07/25 00:56:52
[sftp-client.c sftp-client.h sftp.1 sftp.c] sftp support for resuming partial downloads; patch mostly by Loganaden Velvindron/AfriNIC with some tweaks by me; feedback and ok dtucker@
Diffstat (limited to 'sftp-client.c')
-rw-r--r--sftp-client.c75
1 files changed, 52 insertions, 23 deletions
diff --git a/sftp-client.c b/sftp-client.c
index ab035c713..cb4efd3ea 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.100 2013/06/01 22:34:50 dtucker Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.101 2013/07/25 00:56:51 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -112,7 +112,7 @@ send_msg(struct sftp_conn *conn, Buffer *m)
112 iov[1].iov_len = buffer_len(m); 112 iov[1].iov_len = buffer_len(m);
113 113
114 if (atomiciov6(writev, conn->fd_out, iov, 2, 114 if (atomiciov6(writev, conn->fd_out, iov, 2,
115 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) != 115 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
116 buffer_len(m) + sizeof(mlen)) 116 buffer_len(m) + sizeof(mlen))
117 fatal("Couldn't send packet: %s", strerror(errno)); 117 fatal("Couldn't send packet: %s", strerror(errno));
118 118
@@ -988,16 +988,17 @@ send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
988 988
989int 989int
990do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 990do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
991 Attrib *a, int pflag) 991 Attrib *a, int pflag, int resume)
992{ 992{
993 Attrib junk; 993 Attrib junk;
994 Buffer msg; 994 Buffer msg;
995 char *handle; 995 char *handle;
996 int local_fd, status = 0, write_error; 996 int local_fd = -1, status = 0, write_error;
997 int read_error, write_errno; 997 int read_error, write_errno, reordered = 0;
998 u_int64_t offset, size; 998 u_int64_t offset = 0, size, highwater;
999 u_int handle_len, mode, type, id, buflen, num_req, max_req; 999 u_int handle_len, mode, type, id, buflen, num_req, max_req;
1000 off_t progress_counter; 1000 off_t progress_counter;
1001 struct stat st;
1001 struct request { 1002 struct request {
1002 u_int id; 1003 u_int id;
1003 u_int len; 1004 u_int len;
@@ -1050,21 +1051,36 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1050 return(-1); 1051 return(-1);
1051 } 1052 }
1052 1053
1053 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, 1054 local_fd = open(local_path, O_WRONLY | O_CREAT | (resume ? : O_TRUNC),
1054 mode | S_IWUSR); 1055 mode | S_IWUSR);
1055 if (local_fd == -1) { 1056 if (local_fd == -1) {
1056 error("Couldn't open local file \"%s\" for writing: %s", 1057 error("Couldn't open local file \"%s\" for writing: %s",
1057 local_path, strerror(errno)); 1058 local_path, strerror(errno));
1058 do_close(conn, handle, handle_len); 1059 goto fail;
1059 buffer_free(&msg); 1060 }
1060 free(handle); 1061 offset = highwater = 0;
1061 return(-1); 1062 if (resume) {
1063 if (fstat(local_fd, &st) == -1) {
1064 error("Unable to stat local file \"%s\": %s",
1065 local_path, strerror(errno));
1066 goto fail;
1067 }
1068 if ((size_t)st.st_size > size) {
1069 error("Unable to resume download of \"%s\": "
1070 "local file is larger than remote", local_path);
1071 fail:
1072 do_close(conn, handle, handle_len);
1073 buffer_free(&msg);
1074 free(handle);
1075 return -1;
1076 }
1077 offset = highwater = st.st_size;
1062 } 1078 }
1063 1079
1064 /* Read from remote and write to local */ 1080 /* Read from remote and write to local */
1065 write_error = read_error = write_errno = num_req = offset = 0; 1081 write_error = read_error = write_errno = num_req = 0;
1066 max_req = 1; 1082 max_req = 1;
1067 progress_counter = 0; 1083 progress_counter = offset;
1068 1084
1069 if (showprogress && size != 0) 1085 if (showprogress && size != 0)
1070 start_progress_meter(remote_path, size, &progress_counter); 1086 start_progress_meter(remote_path, size, &progress_counter);
@@ -1139,6 +1155,10 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1139 write_error = 1; 1155 write_error = 1;
1140 max_req = 0; 1156 max_req = 0;
1141 } 1157 }
1158 else if (!reordered && req->offset <= highwater)
1159 highwater = req->offset + len;
1160 else if (!reordered && req->offset > highwater)
1161 reordered = 1;
1142 progress_counter += len; 1162 progress_counter += len;
1143 free(data); 1163 free(data);
1144 1164
@@ -1187,7 +1207,15 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1187 /* Sanity check */ 1207 /* Sanity check */
1188 if (TAILQ_FIRST(&requests) != NULL) 1208 if (TAILQ_FIRST(&requests) != NULL)
1189 fatal("Transfer complete, but requests still in queue"); 1209 fatal("Transfer complete, but requests still in queue");
1190 1210 /* Truncate at highest contiguous point to avoid holes on interrupt */
1211 if (read_error || write_error || interrupted) {
1212 if (reordered && resume) {
1213 error("Unable to resume download of \"%s\": "
1214 "server reordered requests", local_path);
1215 }
1216 debug("truncating at %llu", (unsigned long long)highwater);
1217 ftruncate(local_fd, highwater);
1218 }
1191 if (read_error) { 1219 if (read_error) {
1192 error("Couldn't read from remote file \"%s\" : %s", 1220 error("Couldn't read from remote file \"%s\" : %s",
1193 remote_path, fx2txt(status)); 1221 remote_path, fx2txt(status));
@@ -1199,7 +1227,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1199 do_close(conn, handle, handle_len); 1227 do_close(conn, handle, handle_len);
1200 } else { 1228 } else {
1201 status = do_close(conn, handle, handle_len); 1229 status = do_close(conn, handle, handle_len);
1202 1230 if (interrupted)
1231 status = -1;
1203 /* Override umask and utimes if asked */ 1232 /* Override umask and utimes if asked */
1204#ifdef HAVE_FCHMOD 1233#ifdef HAVE_FCHMOD
1205 if (pflag && fchmod(local_fd, mode) == -1) 1234 if (pflag && fchmod(local_fd, mode) == -1)
@@ -1227,7 +1256,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1227 1256
1228static int 1257static int
1229download_dir_internal(struct sftp_conn *conn, char *src, char *dst, 1258download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1230 Attrib *dirattrib, int pflag, int printflag, int depth) 1259 Attrib *dirattrib, int pflag, int printflag, int depth, int resume)
1231{ 1260{
1232 int i, ret = 0; 1261 int i, ret = 0;
1233 SFTP_DIRENT **dir_entries; 1262 SFTP_DIRENT **dir_entries;
@@ -1280,11 +1309,11 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1280 continue; 1309 continue;
1281 if (download_dir_internal(conn, new_src, new_dst, 1310 if (download_dir_internal(conn, new_src, new_dst,
1282 &(dir_entries[i]->a), pflag, printflag, 1311 &(dir_entries[i]->a), pflag, printflag,
1283 depth + 1) == -1) 1312 depth + 1, resume) == -1)
1284 ret = -1; 1313 ret = -1;
1285 } else if (S_ISREG(dir_entries[i]->a.perm) ) { 1314 } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1286 if (do_download(conn, new_src, new_dst, 1315 if (do_download(conn, new_src, new_dst,
1287 &(dir_entries[i]->a), pflag) == -1) { 1316 &(dir_entries[i]->a), pflag, resume) == -1) {
1288 error("Download of file %s to %s failed", 1317 error("Download of file %s to %s failed",
1289 new_src, new_dst); 1318 new_src, new_dst);
1290 ret = -1; 1319 ret = -1;
@@ -1317,7 +1346,7 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1317 1346
1318int 1347int
1319download_dir(struct sftp_conn *conn, char *src, char *dst, 1348download_dir(struct sftp_conn *conn, char *src, char *dst,
1320 Attrib *dirattrib, int pflag, int printflag) 1349 Attrib *dirattrib, int pflag, int printflag, int resume)
1321{ 1350{
1322 char *src_canon; 1351 char *src_canon;
1323 int ret; 1352 int ret;
@@ -1328,7 +1357,7 @@ download_dir(struct sftp_conn *conn, char *src, char *dst,
1328 } 1357 }
1329 1358
1330 ret = download_dir_internal(conn, src_canon, dst, 1359 ret = download_dir_internal(conn, src_canon, dst,
1331 dirattrib, pflag, printflag, 0); 1360 dirattrib, pflag, printflag, 0, resume);
1332 free(src_canon); 1361 free(src_canon);
1333 return ret; 1362 return ret;
1334} 1363}
@@ -1553,7 +1582,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1553 a.perm &= 01777; 1582 a.perm &= 01777;
1554 if (!pflag) 1583 if (!pflag)
1555 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1584 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1556 1585
1557 status = do_mkdir(conn, dst, &a, 0); 1586 status = do_mkdir(conn, dst, &a, 0);
1558 /* 1587 /*
1559 * we lack a portable status for errno EEXIST, 1588 * we lack a portable status for errno EEXIST,
@@ -1563,7 +1592,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1563 if (status != SSH2_FX_OK) { 1592 if (status != SSH2_FX_OK) {
1564 if (status != SSH2_FX_FAILURE) 1593 if (status != SSH2_FX_FAILURE)
1565 return -1; 1594 return -1;
1566 if (do_stat(conn, dst, 0) == NULL) 1595 if (do_stat(conn, dst, 0) == NULL)
1567 return -1; 1596 return -1;
1568 } 1597 }
1569 1598
@@ -1571,7 +1600,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1571 error("Failed to open dir \"%s\": %s", src, strerror(errno)); 1600 error("Failed to open dir \"%s\": %s", src, strerror(errno));
1572 return -1; 1601 return -1;
1573 } 1602 }
1574 1603
1575 while (((dp = readdir(dirp)) != NULL) && !interrupted) { 1604 while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1576 if (dp->d_ino == 0) 1605 if (dp->d_ino == 0)
1577 continue; 1606 continue;