| diff --color -ru a/scp.c b/scp.c |
| |
| |
| @@ -1324,12 +1324,12 @@ |
| |
| 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 @@ |
| 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 -ru a/sftp.c b/sftp.c |
| |
| |
| @@ -666,12 +666,12 @@ |
| 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 @@ |
| 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 -ru a/sftp-client.c b/sftp-client.c |
| |
| |
| @@ -1454,7 +1454,7 @@ |
| 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; |
| @@ -1661,8 +1661,11 @@ |
| /* 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 @@ |
| 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 @@ |
| 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 @@ |
| 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 @@ |
| 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 @@ |
| |
| 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 @@ |
| } |
| } |
| |
| + 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 @@ |
| 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 @@ |
| 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 @@ |
| 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 @@ |
| |
| 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 @@ |
| 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 @@ |
| } |
| |
| 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 -ru a/sftp-client.h b/sftp-client.h |
| |
| |
| @@ -138,28 +138,29 @@ |
| * 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 |