You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
11 KiB
304 lines
11 KiB
diff --color -rup a/scp.c b/scp.c |
|
--- a/scp.c 2022-07-26 14:51:40.560120817 +0200 |
|
+++ b/scp.c 2022-07-26 14:52:37.118213004 +0200 |
|
@@ -1324,12 +1324,12 @@ source_sftp(int argc, char *src, char *t |
|
|
|
if (src_is_dir && iamrecursive) { |
|
if (upload_dir(conn, src, abs_dst, pflag, |
|
- SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) { |
|
+ SFTP_PROGRESS_ONLY, 0, 0, 1, 1, 1) != 0) { |
|
error("failed to upload directory %s to %s", |
|
src, abs_dst); |
|
errs = 1; |
|
} |
|
- } else if (do_upload(conn, src, abs_dst, pflag, 0, 0) != 0) { |
|
+ } else if (do_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) { |
|
error("failed to upload file %s to %s", src, abs_dst); |
|
errs = 1; |
|
} |
|
@@ -1566,11 +1566,11 @@ sink_sftp(int argc, char *dst, const cha |
|
debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); |
|
if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { |
|
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, |
|
- pflag, SFTP_PROGRESS_ONLY, 0, 0, 1) == -1) |
|
+ pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1) |
|
err = -1; |
|
} else { |
|
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, |
|
- pflag, 0, 0) == -1) |
|
+ pflag, 0, 0, 1) == -1) |
|
err = -1; |
|
} |
|
free(abs_dst); |
|
diff --color -rup a/sftp.c b/sftp.c |
|
--- a/sftp.c 2022-07-26 14:51:40.561120836 +0200 |
|
+++ b/sftp.c 2022-07-26 14:52:37.119213023 +0200 |
|
@@ -666,12 +666,12 @@ process_get(struct sftp_conn *conn, cons |
|
if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { |
|
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, |
|
pflag || global_pflag, 1, resume, |
|
- fflag || global_fflag, 0) == -1) |
|
+ fflag || global_fflag, 0, 0) == -1) |
|
err = -1; |
|
} else { |
|
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, |
|
pflag || global_pflag, resume, |
|
- fflag || global_fflag) == -1) |
|
+ fflag || global_fflag, 0) == -1) |
|
err = -1; |
|
} |
|
free(abs_dst); |
|
@@ -760,12 +760,12 @@ process_put(struct sftp_conn *conn, cons |
|
if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { |
|
if (upload_dir(conn, g.gl_pathv[i], abs_dst, |
|
pflag || global_pflag, 1, resume, |
|
- fflag || global_fflag, 0, 0) == -1) |
|
+ fflag || global_fflag, 0, 0, 0) == -1) |
|
err = -1; |
|
} else { |
|
if (do_upload(conn, g.gl_pathv[i], abs_dst, |
|
pflag || global_pflag, resume, |
|
- fflag || global_fflag) == -1) |
|
+ fflag || global_fflag, 0) == -1) |
|
err = -1; |
|
} |
|
} |
|
diff --color -rup a/sftp-client.c b/sftp-client.c |
|
--- a/sftp-client.c 2022-07-26 14:51:40.561120836 +0200 |
|
+++ b/sftp-client.c 2022-07-26 15:09:54.825295533 +0200 |
|
@@ -1454,7 +1454,7 @@ progress_meter_path(const char *path) |
|
int |
|
do_download(struct sftp_conn *conn, const char *remote_path, |
|
const char *local_path, Attrib *a, int preserve_flag, int resume_flag, |
|
- int fsync_flag) |
|
+ int fsync_flag, int inplace_flag) |
|
{ |
|
struct sshbuf *msg; |
|
u_char *handle; |
|
@@ -1498,8 +1498,8 @@ do_download(struct sftp_conn *conn, cons |
|
&handle, &handle_len) != 0) |
|
return -1; |
|
|
|
- local_fd = open(local_path, |
|
- O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR); |
|
+ local_fd = open(local_path, O_WRONLY | O_CREAT | |
|
+ ((resume_flag || inplace_flag) ? 0 : O_TRUNC), mode | S_IWUSR); |
|
if (local_fd == -1) { |
|
error("Couldn't open local file \"%s\" for writing: %s", |
|
local_path, strerror(errno)); |
|
@@ -1661,8 +1661,11 @@ do_download(struct sftp_conn *conn, cons |
|
/* 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) { |
|
+ /* |
|
+ * Truncate at highest contiguous point to avoid holes on interrupt, |
|
+ * or unconditionally if writing in place. |
|
+ */ |
|
+ if (inplace_flag || read_error || write_error || interrupted) { |
|
if (reordered && resume_flag) { |
|
error("Unable to resume download of \"%s\": " |
|
"server reordered requests", local_path); |
|
@@ -1724,7 +1727,7 @@ do_download(struct sftp_conn *conn, cons |
|
static int |
|
download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, |
|
int depth, Attrib *dirattrib, int preserve_flag, int print_flag, |
|
- int resume_flag, int fsync_flag, int follow_link_flag) |
|
+ int resume_flag, int fsync_flag, int follow_link_flag, int inplace_flag) |
|
{ |
|
int i, ret = 0; |
|
SFTP_DIRENT **dir_entries; |
|
@@ -1781,7 +1784,7 @@ download_dir_internal(struct sftp_conn * |
|
if (download_dir_internal(conn, new_src, new_dst, |
|
depth + 1, &(dir_entries[i]->a), preserve_flag, |
|
print_flag, resume_flag, |
|
- fsync_flag, follow_link_flag) == -1) |
|
+ fsync_flag, follow_link_flag, inplace_flag) == -1) |
|
ret = -1; |
|
} else if (S_ISREG(dir_entries[i]->a.perm) || |
|
(follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { |
|
@@ -1793,7 +1796,8 @@ download_dir_internal(struct sftp_conn * |
|
if (do_download(conn, new_src, new_dst, |
|
S_ISLNK(dir_entries[i]->a.perm) ? NULL : |
|
&(dir_entries[i]->a), |
|
- preserve_flag, resume_flag, fsync_flag) == -1) { |
|
+ preserve_flag, resume_flag, fsync_flag, |
|
+ inplace_flag) == -1) { |
|
error("Download of file %s to %s failed", |
|
new_src, new_dst); |
|
ret = -1; |
|
@@ -1831,7 +1835,7 @@ download_dir_internal(struct sftp_conn * |
|
int |
|
download_dir(struct sftp_conn *conn, const char *src, const char *dst, |
|
Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, |
|
- int fsync_flag, int follow_link_flag) |
|
+ int fsync_flag, int follow_link_flag, int inplace_flag) |
|
{ |
|
char *src_canon; |
|
int ret; |
|
@@ -1843,26 +1847,25 @@ download_dir(struct sftp_conn *conn, con |
|
|
|
ret = download_dir_internal(conn, src_canon, dst, 0, |
|
dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag, |
|
- follow_link_flag); |
|
+ follow_link_flag, inplace_flag); |
|
free(src_canon); |
|
return ret; |
|
} |
|
|
|
int |
|
do_upload(struct sftp_conn *conn, const char *local_path, |
|
- const char *remote_path, int preserve_flag, int resume, int fsync_flag) |
|
+ const char *remote_path, int preserve_flag, int resume, |
|
+ int fsync_flag, int inplace_flag) |
|
{ |
|
int r, local_fd; |
|
- u_int status = SSH2_FX_OK; |
|
- u_int id; |
|
- u_char type; |
|
+ u_int openmode, id, status = SSH2_FX_OK, reordered = 0; |
|
off_t offset, progress_counter; |
|
- u_char *handle, *data; |
|
+ u_char type, *handle, *data; |
|
struct sshbuf *msg; |
|
struct stat sb; |
|
- Attrib a, *c = NULL; |
|
- u_int32_t startid; |
|
- u_int32_t ackid; |
|
+ Attrib a, t, *c = NULL; |
|
+ u_int32_t startid, ackid; |
|
+ u_int64_t highwater = 0; |
|
struct request *ack = NULL; |
|
struct requests acks; |
|
size_t handle_len; |
|
@@ -1913,10 +1916,15 @@ do_upload(struct sftp_conn *conn, const |
|
} |
|
} |
|
|
|
+ openmode = SSH2_FXF_WRITE|SSH2_FXF_CREAT; |
|
+ if (resume) |
|
+ openmode |= SSH2_FXF_APPEND; |
|
+ else if (!inplace_flag) |
|
+ openmode |= SSH2_FXF_TRUNC; |
|
+ |
|
/* Send open request */ |
|
- if (send_open(conn, remote_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT| |
|
- (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC), |
|
- &a, &handle, &handle_len) != 0) { |
|
+ if (send_open(conn, remote_path, "dest", openmode, &a, |
|
+ &handle, &handle_len) != 0) { |
|
close(local_fd); |
|
return -1; |
|
} |
|
@@ -1999,6 +2007,12 @@ do_upload(struct sftp_conn *conn, const |
|
ack->id, ack->len, (unsigned long long)ack->offset); |
|
++ackid; |
|
progress_counter += ack->len; |
|
+ if (!reordered && ack->offset <= highwater) |
|
+ highwater = ack->offset + ack->len; |
|
+ else if (!reordered && ack->offset > highwater) { |
|
+ debug3_f("server reordered ACKs"); |
|
+ reordered = 1; |
|
+ } |
|
free(ack); |
|
} |
|
offset += len; |
|
@@ -2017,6 +2031,14 @@ do_upload(struct sftp_conn *conn, const |
|
status = SSH2_FX_FAILURE; |
|
} |
|
|
|
+ if (inplace_flag || (resume && (status != SSH2_FX_OK || interrupted))) { |
|
+ debug("truncating at %llu", (unsigned long long)highwater); |
|
+ attrib_clear(&t); |
|
+ t.flags = SSH2_FILEXFER_ATTR_SIZE; |
|
+ t.size = highwater; |
|
+ do_fsetstat(conn, handle, handle_len, &t); |
|
+ } |
|
+ |
|
if (close(local_fd) == -1) { |
|
error("Couldn't close local file \"%s\": %s", local_path, |
|
strerror(errno)); |
|
@@ -2041,7 +2063,7 @@ do_upload(struct sftp_conn *conn, const |
|
static int |
|
upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, |
|
int depth, int preserve_flag, int print_flag, int resume, int fsync_flag, |
|
- int follow_link_flag) |
|
+ int follow_link_flag, int inplace_flag) |
|
{ |
|
int ret = 0; |
|
DIR *dirp; |
|
@@ -2119,12 +2141,13 @@ upload_dir_internal(struct sftp_conn *co |
|
|
|
if (upload_dir_internal(conn, new_src, new_dst, |
|
depth + 1, preserve_flag, print_flag, resume, |
|
- fsync_flag, follow_link_flag) == -1) |
|
+ fsync_flag, follow_link_flag, inplace_flag) == -1) |
|
ret = -1; |
|
} else if (S_ISREG(sb.st_mode) || |
|
(follow_link_flag && S_ISLNK(sb.st_mode))) { |
|
if (do_upload(conn, new_src, new_dst, |
|
- preserve_flag, resume, fsync_flag) == -1) { |
|
+ preserve_flag, resume, fsync_flag, |
|
+ inplace_flag) == -1) { |
|
error("Uploading of file %s to %s failed!", |
|
new_src, new_dst); |
|
ret = -1; |
|
@@ -2144,7 +2167,7 @@ upload_dir_internal(struct sftp_conn *co |
|
int |
|
upload_dir(struct sftp_conn *conn, const char *src, const char *dst, |
|
int preserve_flag, int print_flag, int resume, int fsync_flag, |
|
- int follow_link_flag, int create_dir) |
|
+ int follow_link_flag, int create_dir, int inplace_flag) |
|
{ |
|
char *dst_canon; |
|
int ret; |
|
@@ -2155,7 +2178,7 @@ upload_dir(struct sftp_conn *conn, const |
|
} |
|
|
|
ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, |
|
- print_flag, resume, fsync_flag, follow_link_flag); |
|
+ print_flag, resume, fsync_flag, follow_link_flag, inplace_flag); |
|
|
|
free(dst_canon); |
|
return ret; |
|
diff --color -rup a/sftp-client.h b/sftp-client.h |
|
--- a/sftp-client.h 2022-07-26 14:51:40.561120836 +0200 |
|
+++ b/sftp-client.h 2022-07-26 14:52:37.120213042 +0200 |
|
@@ -138,28 +138,29 @@ int do_fsync(struct sftp_conn *conn, u_c |
|
* Download 'remote_path' to 'local_path'. Preserve permissions and times |
|
* if 'pflag' is set |
|
*/ |
|
-int do_download(struct sftp_conn *, const char *, const char *, |
|
- Attrib *, int, int, int); |
|
+int do_download(struct sftp_conn *, const char *, const char *, Attrib *, |
|
+ int, int, int, int); |
|
|
|
/* |
|
* Recursively download 'remote_directory' to 'local_directory'. Preserve |
|
* times if 'pflag' is set |
|
*/ |
|
-int download_dir(struct sftp_conn *, const char *, const char *, |
|
- Attrib *, int, int, int, int, int); |
|
+int download_dir(struct sftp_conn *, const char *, const char *, Attrib *, |
|
+ int, int, int, int, int, int); |
|
|
|
/* |
|
* Upload 'local_path' to 'remote_path'. Preserve permissions and times |
|
* if 'pflag' is set |
|
*/ |
|
-int do_upload(struct sftp_conn *, const char *, const char *, int, int, int); |
|
+int do_upload(struct sftp_conn *, const char *, const char *, |
|
+ int, int, int, int); |
|
|
|
/* |
|
* Recursively upload 'local_directory' to 'remote_directory'. Preserve |
|
* times if 'pflag' is set |
|
*/ |
|
-int upload_dir(struct sftp_conn *, const char *, const char *, int, int, int, |
|
- int, int, int); |
|
+int upload_dir(struct sftp_conn *, const char *, const char *, |
|
+ int, int, int, int, int, int, int); |
|
|
|
/* |
|
* Download a 'from_path' from the 'from' connection and upload it to
|
|
|