summaryrefslogtreecommitdiffstats
path: root/crypto/openssh/sftp-client.c
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>2013-09-21 21:36:09 +0000
committerdes <des@FreeBSD.org>2013-09-21 21:36:09 +0000
commitcda41f674dcdd03b86d4e53cd0dbd08b534f9e7a (patch)
treea72d0647ecce54e017341118e11204ee4ba0de68 /crypto/openssh/sftp-client.c
parent3e16db31d109ec87db0aa5d7fcd63e93398259d4 (diff)
parentff2597d3eebc3da3f7cf2a638607274cad9b199e (diff)
downloadFreeBSD-src-cda41f674dcdd03b86d4e53cd0dbd08b534f9e7a.zip
FreeBSD-src-cda41f674dcdd03b86d4e53cd0dbd08b534f9e7a.tar.gz
Upgrade to 6.3p1.
Approved by: re (gjb)
Diffstat (limited to 'crypto/openssh/sftp-client.c')
-rw-r--r--crypto/openssh/sftp-client.c135
1 files changed, 83 insertions, 52 deletions
diff --git a/crypto/openssh/sftp-client.c b/crypto/openssh/sftp-client.c
index 85f2bd4..f4f1970 100644
--- a/crypto/openssh/sftp-client.c
+++ b/crypto/openssh/sftp-client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.c,v 1.97 2012/07/02 12:13:26 dtucker Exp $ */
+/* $OpenBSD: sftp-client.c,v 1.101 2013/07/25 00:56:51 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
@@ -112,7 +112,7 @@ send_msg(struct sftp_conn *conn, Buffer *m)
iov[1].iov_len = buffer_len(m);
if (atomiciov6(writev, conn->fd_out, iov, 2,
- conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
+ conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
buffer_len(m) + sizeof(mlen))
fatal("Couldn't send packet: %s", strerror(errno));
@@ -394,8 +394,8 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
} else {
debug2("Unrecognised server extension \"%s\"", name);
}
- xfree(name);
- xfree(value);
+ free(name);
+ free(value);
}
buffer_free(&msg);
@@ -509,7 +509,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
error("Couldn't read directory: %s",
fx2txt(status));
do_close(conn, handle, handle_len);
- xfree(handle);
+ free(handle);
buffer_free(&msg);
return(status);
}
@@ -552,14 +552,14 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
(*dir)[++ents] = NULL;
}
next:
- xfree(filename);
- xfree(longname);
+ free(filename);
+ free(longname);
}
}
buffer_free(&msg);
do_close(conn, handle, handle_len);
- xfree(handle);
+ free(handle);
/* Don't return partial matches on interrupt */
if (interrupted && dir != NULL && *dir != NULL) {
@@ -582,11 +582,11 @@ void free_sftp_dirents(SFTP_DIRENT **s)
int i;
for (i = 0; s[i]; i++) {
- xfree(s[i]->filename);
- xfree(s[i]->longname);
- xfree(s[i]);
+ free(s[i]->filename);
+ free(s[i]->longname);
+ free(s[i]);
}
- xfree(s);
+ free(s);
}
int
@@ -760,7 +760,7 @@ do_realpath(struct sftp_conn *conn, char *path)
debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename,
(unsigned long)a->size);
- xfree(longname);
+ free(longname);
buffer_free(&msg);
@@ -907,7 +907,7 @@ do_readlink(struct sftp_conn *conn, char *path)
debug3("SSH_FXP_READLINK %s -> %s", path, filename);
- xfree(longname);
+ free(longname);
buffer_free(&msg);
@@ -988,16 +988,17 @@ send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
int
do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
- Attrib *a, int pflag)
+ Attrib *a, int pflag, int resume)
{
Attrib junk;
Buffer msg;
char *handle;
- int local_fd, status = 0, write_error;
- int read_error, write_errno;
- u_int64_t offset, size;
+ int local_fd = -1, status = 0, write_error;
+ int read_error, write_errno, reordered = 0;
+ u_int64_t offset = 0, size, highwater;
u_int handle_len, mode, type, id, buflen, num_req, max_req;
off_t progress_counter;
+ struct stat st;
struct request {
u_int id;
u_int len;
@@ -1050,21 +1051,36 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
return(-1);
}
- local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
- mode | S_IWRITE);
+ local_fd = open(local_path, O_WRONLY | O_CREAT | (resume ? 0 : O_TRUNC),
+ mode | S_IWUSR);
if (local_fd == -1) {
error("Couldn't open local file \"%s\" for writing: %s",
local_path, strerror(errno));
- do_close(conn, handle, handle_len);
- buffer_free(&msg);
- xfree(handle);
- return(-1);
+ goto fail;
+ }
+ offset = highwater = 0;
+ if (resume) {
+ if (fstat(local_fd, &st) == -1) {
+ error("Unable to stat local file \"%s\": %s",
+ local_path, strerror(errno));
+ goto fail;
+ }
+ if ((size_t)st.st_size > size) {
+ error("Unable to resume download of \"%s\": "
+ "local file is larger than remote", local_path);
+ fail:
+ do_close(conn, handle, handle_len);
+ buffer_free(&msg);
+ free(handle);
+ return -1;
+ }
+ offset = highwater = st.st_size;
}
/* Read from remote and write to local */
- write_error = read_error = write_errno = num_req = offset = 0;
+ write_error = read_error = write_errno = num_req = 0;
max_req = 1;
- progress_counter = 0;
+ progress_counter = offset;
if (showprogress && size != 0)
start_progress_meter(remote_path, size, &progress_counter);
@@ -1121,7 +1137,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
read_error = 1;
max_req = 0;
TAILQ_REMOVE(&requests, req, tq);
- xfree(req);
+ free(req);
num_req--;
break;
case SSH2_FXP_DATA:
@@ -1139,12 +1155,16 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
write_error = 1;
max_req = 0;
}
+ else if (!reordered && req->offset <= highwater)
+ highwater = req->offset + len;
+ else if (!reordered && req->offset > highwater)
+ reordered = 1;
progress_counter += len;
- xfree(data);
+ free(data);
if (len == req->len) {
TAILQ_REMOVE(&requests, req, tq);
- xfree(req);
+ free(req);
num_req--;
} else {
/* Resend the request for the missing data */
@@ -1187,7 +1207,15 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
/* Sanity check */
if (TAILQ_FIRST(&requests) != NULL)
fatal("Transfer complete, but requests still in queue");
-
+ /* Truncate at highest contiguous point to avoid holes on interrupt */
+ if (read_error || write_error || interrupted) {
+ if (reordered && resume) {
+ error("Unable to resume download of \"%s\": "
+ "server reordered requests", local_path);
+ }
+ debug("truncating at %llu", (unsigned long long)highwater);
+ ftruncate(local_fd, highwater);
+ }
if (read_error) {
error("Couldn't read from remote file \"%s\" : %s",
remote_path, fx2txt(status));
@@ -1199,7 +1227,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
do_close(conn, handle, handle_len);
} else {
status = do_close(conn, handle, handle_len);
-
+ if (interrupted)
+ status = -1;
/* Override umask and utimes if asked */
#ifdef HAVE_FCHMOD
if (pflag && fchmod(local_fd, mode) == -1)
@@ -1220,14 +1249,14 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
}
close(local_fd);
buffer_free(&msg);
- xfree(handle);
+ free(handle);
return(status);
}
static int
download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
- Attrib *dirattrib, int pflag, int printflag, int depth)
+ Attrib *dirattrib, int pflag, int printflag, int depth, int resume)
{
int i, ret = 0;
SFTP_DIRENT **dir_entries;
@@ -1280,11 +1309,11 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
continue;
if (download_dir_internal(conn, new_src, new_dst,
&(dir_entries[i]->a), pflag, printflag,
- depth + 1) == -1)
+ depth + 1, resume) == -1)
ret = -1;
} else if (S_ISREG(dir_entries[i]->a.perm) ) {
if (do_download(conn, new_src, new_dst,
- &(dir_entries[i]->a), pflag) == -1) {
+ &(dir_entries[i]->a), pflag, resume) == -1) {
error("Download of file %s to %s failed",
new_src, new_dst);
ret = -1;
@@ -1292,8 +1321,8 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
} else
logit("%s: not a regular file\n", new_src);
- xfree(new_dst);
- xfree(new_src);
+ free(new_dst);
+ free(new_src);
}
if (pflag) {
@@ -1317,7 +1346,7 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
int
download_dir(struct sftp_conn *conn, char *src, char *dst,
- Attrib *dirattrib, int pflag, int printflag)
+ Attrib *dirattrib, int pflag, int printflag, int resume)
{
char *src_canon;
int ret;
@@ -1328,8 +1357,8 @@ download_dir(struct sftp_conn *conn, char *src, char *dst,
}
ret = download_dir_internal(conn, src_canon, dst,
- dirattrib, pflag, printflag, 0);
- xfree(src_canon);
+ dirattrib, pflag, printflag, 0, resume);
+ free(src_canon);
return ret;
}
@@ -1340,7 +1369,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
int local_fd;
int status = SSH2_FX_OK;
u_int handle_len, id, type;
- off_t offset;
+ off_t offset, progress_counter;
char *handle, *data;
Buffer msg;
struct stat sb;
@@ -1408,9 +1437,10 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
data = xmalloc(conn->transfer_buflen);
/* Read from local and write to remote */
- offset = 0;
+ offset = progress_counter = 0;
if (showprogress)
- start_progress_meter(local_path, sb.st_size, &offset);
+ start_progress_meter(local_path, sb.st_size,
+ &progress_counter);
for (;;) {
int len;
@@ -1481,7 +1511,8 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
debug3("In write loop, ack for %u %u bytes at %lld",
ack->id, ack->len, (long long)ack->offset);
++ackid;
- xfree(ack);
+ progress_counter += ack->len;
+ free(ack);
}
offset += len;
if (offset < 0)
@@ -1491,7 +1522,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
if (showprogress)
stop_progress_meter();
- xfree(data);
+ free(data);
if (status != SSH2_FX_OK) {
error("Couldn't write to remote file \"%s\": %s",
@@ -1511,7 +1542,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
status = -1;
- xfree(handle);
+ free(handle);
return status;
}
@@ -1551,7 +1582,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
a.perm &= 01777;
if (!pflag)
a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
-
+
status = do_mkdir(conn, dst, &a, 0);
/*
* we lack a portable status for errno EEXIST,
@@ -1561,7 +1592,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
if (status != SSH2_FX_OK) {
if (status != SSH2_FX_FAILURE)
return -1;
- if (do_stat(conn, dst, 0) == NULL)
+ if (do_stat(conn, dst, 0) == NULL)
return -1;
}
@@ -1569,7 +1600,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
error("Failed to open dir \"%s\": %s", src, strerror(errno));
return -1;
}
-
+
while (((dp = readdir(dirp)) != NULL) && !interrupted) {
if (dp->d_ino == 0)
continue;
@@ -1597,8 +1628,8 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
}
} else
logit("%s: not a regular file\n", filename);
- xfree(new_dst);
- xfree(new_src);
+ free(new_dst);
+ free(new_src);
}
do_setstat(conn, dst, &a);
@@ -1620,7 +1651,7 @@ upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
}
ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
- xfree(dst_canon);
+ free(dst_canon);
return ret;
}
OpenPOWER on IntegriCloud