Zoltan Fridrich c958ea
diff --color -ru a/scp.c b/scp.c
Zoltan Fridrich c958ea
--- a/scp.c	2022-06-30 09:47:39.529662574 +0200
Zoltan Fridrich c958ea
+++ b/scp.c	2022-06-30 09:51:05.740719310 +0200
Zoltan Fridrich c958ea
@@ -1324,12 +1324,12 @@
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 	if (src_is_dir && iamrecursive) {
Zoltan Fridrich c958ea
 		if (upload_dir(conn, src, abs_dst, pflag,
Zoltan Fridrich c958ea
-		    SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) {
Zoltan Fridrich c958ea
+		    SFTP_PROGRESS_ONLY, 0, 0, 1, 1, 1) != 0) {
Zoltan Fridrich c958ea
 			error("failed to upload directory %s to %s",
Zoltan Fridrich c958ea
 				src, abs_dst);
Zoltan Fridrich c958ea
 			errs = 1;
Zoltan Fridrich c958ea
 		}
Zoltan Fridrich c958ea
-	} else if (do_upload(conn, src, abs_dst, pflag, 0, 0) != 0) {
Zoltan Fridrich c958ea
+	} else if (do_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) {
Zoltan Fridrich c958ea
 		error("failed to upload file %s to %s", src, abs_dst);
Zoltan Fridrich c958ea
 		errs = 1;
Zoltan Fridrich c958ea
 	}
Zoltan Fridrich c958ea
@@ -1566,11 +1566,11 @@
Zoltan Fridrich c958ea
 		debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
Zoltan Fridrich c958ea
 		if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) {
Zoltan Fridrich c958ea
 			if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
Zoltan Fridrich c958ea
-			    pflag, SFTP_PROGRESS_ONLY, 0, 0, 1) == -1)
Zoltan Fridrich c958ea
+			    pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1)
Zoltan Fridrich c958ea
 				err = -1;
Zoltan Fridrich c958ea
 		} else {
Zoltan Fridrich c958ea
 			if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
Zoltan Fridrich c958ea
-			    pflag, 0, 0) == -1)
Zoltan Fridrich c958ea
+			    pflag, 0, 0, 1) == -1)
Zoltan Fridrich c958ea
 				err = -1;
Zoltan Fridrich c958ea
 		}
Zoltan Fridrich c958ea
 		free(abs_dst);
Zoltan Fridrich c958ea
diff --color -ru a/sftp.c b/sftp.c
Zoltan Fridrich c958ea
--- a/sftp.c	2022-06-30 09:47:39.530662594 +0200
Zoltan Fridrich c958ea
+++ b/sftp.c	2022-06-30 09:52:05.118887441 +0200
Zoltan Fridrich c958ea
@@ -666,12 +666,12 @@
Zoltan Fridrich c958ea
 		if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
Zoltan Fridrich c958ea
 			if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
Zoltan Fridrich c958ea
 			    pflag || global_pflag, 1, resume,
Zoltan Fridrich c958ea
-			    fflag || global_fflag, 0) == -1)
Zoltan Fridrich c958ea
+			    fflag || global_fflag, 0, 0) == -1)
Zoltan Fridrich c958ea
 				err = -1;
Zoltan Fridrich c958ea
 		} else {
Zoltan Fridrich c958ea
 			if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
Zoltan Fridrich c958ea
 			    pflag || global_pflag, resume,
Zoltan Fridrich c958ea
-			    fflag || global_fflag) == -1)
Zoltan Fridrich c958ea
+			    fflag || global_fflag, 0) == -1)
Zoltan Fridrich c958ea
 				err = -1;
Zoltan Fridrich c958ea
 		}
Zoltan Fridrich c958ea
 		free(abs_dst);
Zoltan Fridrich c958ea
@@ -760,12 +760,12 @@
Zoltan Fridrich c958ea
 		if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
Zoltan Fridrich c958ea
 			if (upload_dir(conn, g.gl_pathv[i], abs_dst,
Zoltan Fridrich c958ea
 			    pflag || global_pflag, 1, resume,
Zoltan Fridrich c958ea
-			    fflag || global_fflag, 0, 0) == -1)
Zoltan Fridrich c958ea
+			    fflag || global_fflag, 0, 0, 0) == -1)
Zoltan Fridrich c958ea
 				err = -1;
Zoltan Fridrich c958ea
 		} else {
Zoltan Fridrich c958ea
 			if (do_upload(conn, g.gl_pathv[i], abs_dst,
Zoltan Fridrich c958ea
 			    pflag || global_pflag, resume,
Zoltan Fridrich c958ea
-			    fflag || global_fflag) == -1)
Zoltan Fridrich c958ea
+			    fflag || global_fflag, 0) == -1)
Zoltan Fridrich c958ea
 				err = -1;
Zoltan Fridrich c958ea
 		}
Zoltan Fridrich c958ea
 	}
Zoltan Fridrich c958ea
diff --color -ru a/sftp-client.c b/sftp-client.c
Zoltan Fridrich c958ea
--- a/sftp-client.c	2022-06-30 09:47:39.530662594 +0200
Zoltan Fridrich c958ea
+++ b/sftp-client.c	2022-06-30 10:03:36.608473570 +0200
Zoltan Fridrich c958ea
@@ -1454,7 +1454,7 @@
Zoltan Fridrich c958ea
 int
Zoltan Fridrich c958ea
 do_download(struct sftp_conn *conn, const char *remote_path,
Zoltan Fridrich c958ea
     const char *local_path, Attrib *a, int preserve_flag, int resume_flag,
Zoltan Fridrich c958ea
-    int fsync_flag)
Zoltan Fridrich c958ea
+    int fsync_flag, int inplace_flag)
Zoltan Fridrich c958ea
 {
Zoltan Fridrich c958ea
 	struct sshbuf *msg;
Zoltan Fridrich c958ea
 	u_char *handle;
Zoltan Fridrich c958ea
@@ -1661,8 +1661,11 @@
Zoltan Fridrich c958ea
 	/* Sanity check */
Zoltan Fridrich c958ea
 	if (TAILQ_FIRST(&requests) != NULL)
Zoltan Fridrich c958ea
 		fatal("Transfer complete, but requests still in queue");
Zoltan Fridrich c958ea
-	/* Truncate at highest contiguous point to avoid holes on interrupt */
Zoltan Fridrich c958ea
-	if (read_error || write_error || interrupted) {
Zoltan Fridrich c958ea
+	/*
Zoltan Fridrich c958ea
+	 * Truncate at highest contiguous point to avoid holes on interrupt,
Zoltan Fridrich c958ea
+	 * or unconditionally if writing in place.
Zoltan Fridrich c958ea
+	 */
Zoltan Fridrich c958ea
+	if (inplace_flag || read_error || write_error || interrupted) {
Zoltan Fridrich c958ea
 		if (reordered && resume_flag) {
Zoltan Fridrich c958ea
 			error("Unable to resume download of \"%s\": "
Zoltan Fridrich c958ea
 			    "server reordered requests", local_path);
Zoltan Fridrich c958ea
@@ -1724,7 +1727,7 @@
Zoltan Fridrich c958ea
 static int
Zoltan Fridrich c958ea
 download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
Zoltan Fridrich c958ea
     int depth, Attrib *dirattrib, int preserve_flag, int print_flag,
Zoltan Fridrich c958ea
-    int resume_flag, int fsync_flag, int follow_link_flag)
Zoltan Fridrich c958ea
+    int resume_flag, int fsync_flag, int follow_link_flag, int inplace_flag)
Zoltan Fridrich c958ea
 {
Zoltan Fridrich c958ea
 	int i, ret = 0;
Zoltan Fridrich c958ea
 	SFTP_DIRENT **dir_entries;
Zoltan Fridrich c958ea
@@ -1781,7 +1784,7 @@
Zoltan Fridrich c958ea
 			if (download_dir_internal(conn, new_src, new_dst,
Zoltan Fridrich c958ea
 			    depth + 1, &(dir_entries[i]->a), preserve_flag,
Zoltan Fridrich c958ea
 			    print_flag, resume_flag,
Zoltan Fridrich c958ea
-			    fsync_flag, follow_link_flag) == -1)
Zoltan Fridrich c958ea
+			    fsync_flag, follow_link_flag, inplace_flag) == -1)
Zoltan Fridrich c958ea
 				ret = -1;
Zoltan Fridrich c958ea
 		} else if (S_ISREG(dir_entries[i]->a.perm) ||
Zoltan Fridrich c958ea
 		    (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) {
Zoltan Fridrich c958ea
@@ -1793,7 +1796,8 @@
Zoltan Fridrich c958ea
 			if (do_download(conn, new_src, new_dst,
Zoltan Fridrich c958ea
 			    S_ISLNK(dir_entries[i]->a.perm) ? NULL :
Zoltan Fridrich c958ea
 			    &(dir_entries[i]->a),
Zoltan Fridrich c958ea
-			    preserve_flag, resume_flag, fsync_flag) == -1) {
Zoltan Fridrich c958ea
+			    preserve_flag, resume_flag, fsync_flag,
Zoltan Fridrich c958ea
+			    inplace_flag) == -1) {
Zoltan Fridrich c958ea
 				error("Download of file %s to %s failed",
Zoltan Fridrich c958ea
 				    new_src, new_dst);
Zoltan Fridrich c958ea
 				ret = -1;
Zoltan Fridrich c958ea
@@ -1831,7 +1835,7 @@
Zoltan Fridrich c958ea
 int
Zoltan Fridrich c958ea
 download_dir(struct sftp_conn *conn, const char *src, const char *dst,
Zoltan Fridrich c958ea
     Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
Zoltan Fridrich c958ea
-    int fsync_flag, int follow_link_flag)
Zoltan Fridrich c958ea
+    int fsync_flag, int follow_link_flag, int inplace_flag)
Zoltan Fridrich c958ea
 {
Zoltan Fridrich c958ea
 	char *src_canon;
Zoltan Fridrich c958ea
 	int ret;
Zoltan Fridrich c958ea
@@ -1843,26 +1847,25 @@
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 	ret = download_dir_internal(conn, src_canon, dst, 0,
Zoltan Fridrich c958ea
 	    dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag,
Zoltan Fridrich c958ea
-	    follow_link_flag);
Zoltan Fridrich c958ea
+	    follow_link_flag, inplace_flag);
Zoltan Fridrich c958ea
 	free(src_canon);
Zoltan Fridrich c958ea
 	return ret;
Zoltan Fridrich c958ea
 }
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 int
Zoltan Fridrich c958ea
 do_upload(struct sftp_conn *conn, const char *local_path,
Zoltan Fridrich c958ea
-    const char *remote_path, int preserve_flag, int resume, int fsync_flag)
Zoltan Fridrich c958ea
+    const char *remote_path, int preserve_flag, int resume,
Zoltan Fridrich c958ea
+    int fsync_flag, int inplace_flag)
Zoltan Fridrich c958ea
 {
Zoltan Fridrich c958ea
 	int r, local_fd;
Zoltan Fridrich c958ea
-	u_int status = SSH2_FX_OK;
Zoltan Fridrich c958ea
-	u_int id;
Zoltan Fridrich c958ea
-	u_char type;
Zoltan Fridrich c958ea
+	u_int openmode, id, status = SSH2_FX_OK, reordered = 0;
Zoltan Fridrich c958ea
 	off_t offset, progress_counter;
Zoltan Fridrich c958ea
-	u_char *handle, *data;
Zoltan Fridrich c958ea
+	u_char type, *handle, *data;
Zoltan Fridrich c958ea
 	struct sshbuf *msg;
Zoltan Fridrich c958ea
 	struct stat sb;
Zoltan Fridrich c958ea
-	Attrib a, *c = NULL;
Zoltan Fridrich c958ea
-	u_int32_t startid;
Zoltan Fridrich c958ea
-	u_int32_t ackid;
Zoltan Fridrich c958ea
+	Attrib a, t, *c = NULL;
Zoltan Fridrich c958ea
+	u_int32_t startid, ackid;
Zoltan Fridrich c958ea
+	u_int64_t highwater = 0;
Zoltan Fridrich c958ea
 	struct request *ack = NULL;
Zoltan Fridrich c958ea
 	struct requests acks;
Zoltan Fridrich c958ea
 	size_t handle_len;
Zoltan Fridrich c958ea
@@ -1913,10 +1916,15 @@
Zoltan Fridrich c958ea
 		}
Zoltan Fridrich c958ea
 	}
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
+	openmode = SSH2_FXF_WRITE|SSH2_FXF_CREAT;
Zoltan Fridrich c958ea
+	if (resume)
Zoltan Fridrich c958ea
+		openmode |= SSH2_FXF_APPEND;
Zoltan Fridrich c958ea
+	else if (!inplace_flag)
Zoltan Fridrich c958ea
+		openmode |= SSH2_FXF_TRUNC;
Zoltan Fridrich c958ea
+
Zoltan Fridrich c958ea
 	/* Send open request */
Zoltan Fridrich c958ea
-	if (send_open(conn, remote_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT|
Zoltan Fridrich c958ea
-	    (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC),
Zoltan Fridrich c958ea
-	    &a, &handle, &handle_len) != 0) {
Zoltan Fridrich c958ea
+	if (send_open(conn, remote_path, "dest", openmode, &a,
Zoltan Fridrich c958ea
+	    &handle, &handle_len) != 0) {
Zoltan Fridrich c958ea
 		close(local_fd);
Zoltan Fridrich c958ea
 		return -1;
Zoltan Fridrich c958ea
 	}
Zoltan Fridrich c958ea
@@ -1999,6 +2007,12 @@
Zoltan Fridrich c958ea
 			    ack->id, ack->len, (unsigned long long)ack->offset);
Zoltan Fridrich c958ea
 			++ackid;
Zoltan Fridrich c958ea
 			progress_counter += ack->len;
Zoltan Fridrich c958ea
+			if (!reordered && ack->offset <= highwater)
Zoltan Fridrich c958ea
+				highwater = ack->offset + ack->len;
Zoltan Fridrich c958ea
+			else if (!reordered && ack->offset > highwater) {
Zoltan Fridrich c958ea
+				debug3_f("server reordered ACKs");
Zoltan Fridrich c958ea
+				reordered = 1;
Zoltan Fridrich c958ea
+			}
Zoltan Fridrich c958ea
 			free(ack);
Zoltan Fridrich c958ea
 		}
Zoltan Fridrich c958ea
 		offset += len;
Zoltan Fridrich c958ea
@@ -2017,6 +2031,14 @@
Zoltan Fridrich c958ea
 		status = SSH2_FX_FAILURE;
Zoltan Fridrich c958ea
 	}
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
+	if (inplace_flag || (resume && (status != SSH2_FX_OK || interrupted))) {
Zoltan Fridrich c958ea
+		debug("truncating at %llu", (unsigned long long)highwater);
Zoltan Fridrich c958ea
+		attrib_clear(&t);
Zoltan Fridrich c958ea
+		t.flags = SSH2_FILEXFER_ATTR_SIZE;
Zoltan Fridrich c958ea
+		t.size = highwater;
Zoltan Fridrich c958ea
+		do_fsetstat(conn, handle, handle_len, &t);
Zoltan Fridrich c958ea
+	}
Zoltan Fridrich c958ea
+
Zoltan Fridrich c958ea
 	if (close(local_fd) == -1) {
Zoltan Fridrich c958ea
 		error("Couldn't close local file \"%s\": %s", local_path,
Zoltan Fridrich c958ea
 		    strerror(errno));
Zoltan Fridrich c958ea
@@ -2041,7 +2063,7 @@
Zoltan Fridrich c958ea
 static int
Zoltan Fridrich c958ea
 upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
Zoltan Fridrich c958ea
     int depth, int preserve_flag, int print_flag, int resume, int fsync_flag,
Zoltan Fridrich c958ea
-    int follow_link_flag)
Zoltan Fridrich c958ea
+    int follow_link_flag, int inplace_flag)
Zoltan Fridrich c958ea
 {
Zoltan Fridrich c958ea
 	int ret = 0;
Zoltan Fridrich c958ea
 	DIR *dirp;
Zoltan Fridrich c958ea
@@ -2119,12 +2141,13 @@
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 			if (upload_dir_internal(conn, new_src, new_dst,
Zoltan Fridrich c958ea
 			    depth + 1, preserve_flag, print_flag, resume,
Zoltan Fridrich c958ea
-			    fsync_flag, follow_link_flag) == -1)
Zoltan Fridrich c958ea
+			    fsync_flag, follow_link_flag, inplace_flag) == -1)
Zoltan Fridrich c958ea
 				ret = -1;
Zoltan Fridrich c958ea
 		} else if (S_ISREG(sb.st_mode) ||
Zoltan Fridrich c958ea
 		    (follow_link_flag && S_ISLNK(sb.st_mode))) {
Zoltan Fridrich c958ea
 			if (do_upload(conn, new_src, new_dst,
Zoltan Fridrich c958ea
-			    preserve_flag, resume, fsync_flag) == -1) {
Zoltan Fridrich c958ea
+			    preserve_flag, resume, fsync_flag,
Zoltan Fridrich c958ea
+			    inplace_flag) == -1) {
Zoltan Fridrich c958ea
 				error("Uploading of file %s to %s failed!",
Zoltan Fridrich c958ea
 				    new_src, new_dst);
Zoltan Fridrich c958ea
 				ret = -1;
Zoltan Fridrich c958ea
@@ -2144,7 +2167,7 @@
Zoltan Fridrich c958ea
 int
Zoltan Fridrich c958ea
 upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
Zoltan Fridrich c958ea
     int preserve_flag, int print_flag, int resume, int fsync_flag,
Zoltan Fridrich c958ea
-    int follow_link_flag, int create_dir)
Zoltan Fridrich c958ea
+    int follow_link_flag, int create_dir, int inplace_flag)
Zoltan Fridrich c958ea
 {
Zoltan Fridrich c958ea
 	char *dst_canon;
Zoltan Fridrich c958ea
 	int ret;
Zoltan Fridrich c958ea
@@ -2155,7 +2178,7 @@
Zoltan Fridrich c958ea
 	}
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 	ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
Zoltan Fridrich c958ea
-	    print_flag, resume, fsync_flag, follow_link_flag);
Zoltan Fridrich c958ea
+	    print_flag, resume, fsync_flag, follow_link_flag, inplace_flag);
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 	free(dst_canon);
Zoltan Fridrich c958ea
 	return ret;
Zoltan Fridrich c958ea
diff --color -ru a/sftp-client.h b/sftp-client.h
Zoltan Fridrich c958ea
--- a/sftp-client.h	2022-06-30 09:47:39.530662594 +0200
Zoltan Fridrich c958ea
+++ b/sftp-client.h	2022-06-30 10:05:50.835107759 +0200
Zoltan Fridrich c958ea
@@ -138,28 +138,29 @@
Zoltan Fridrich c958ea
  * Download 'remote_path' to 'local_path'. Preserve permissions and times
Zoltan Fridrich c958ea
  * if 'pflag' is set
Zoltan Fridrich c958ea
  */
Zoltan Fridrich c958ea
-int do_download(struct sftp_conn *, const char *, const char *,
Zoltan Fridrich c958ea
-    Attrib *, int, int, int);
Zoltan Fridrich c958ea
+int do_download(struct sftp_conn *, const char *, const char *, Attrib *,
Zoltan Fridrich c958ea
+    int, int, int, int);
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 /*
Zoltan Fridrich c958ea
  * Recursively download 'remote_directory' to 'local_directory'. Preserve
Zoltan Fridrich c958ea
  * times if 'pflag' is set
Zoltan Fridrich c958ea
  */
Zoltan Fridrich c958ea
-int download_dir(struct sftp_conn *, const char *, const char *,
Zoltan Fridrich c958ea
-    Attrib *, int, int, int, int, int);
Zoltan Fridrich c958ea
+int download_dir(struct sftp_conn *, const char *, const char *, Attrib *,
Zoltan Fridrich c958ea
+    int, int, int, int, int, int);
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 /*
Zoltan Fridrich c958ea
  * Upload 'local_path' to 'remote_path'. Preserve permissions and times
Zoltan Fridrich c958ea
  * if 'pflag' is set
Zoltan Fridrich c958ea
  */
Zoltan Fridrich c958ea
-int do_upload(struct sftp_conn *, const char *, const char *, int, int, int);
Zoltan Fridrich c958ea
+int do_upload(struct sftp_conn *, const char *, const char *,
Zoltan Fridrich c958ea
+    int, int, int, int);
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 /*
Zoltan Fridrich c958ea
  * Recursively upload 'local_directory' to 'remote_directory'. Preserve
Zoltan Fridrich c958ea
  * times if 'pflag' is set
Zoltan Fridrich c958ea
  */
Zoltan Fridrich c958ea
-int upload_dir(struct sftp_conn *, const char *, const char *, int, int, int,
Zoltan Fridrich c958ea
-    int, int, int);
Zoltan Fridrich c958ea
+int upload_dir(struct sftp_conn *, const char *, const char *,
Zoltan Fridrich c958ea
+    int, int, int, int, int, int, int);
Zoltan Fridrich c958ea
 
Zoltan Fridrich c958ea
 /*
Zoltan Fridrich c958ea
  * Download a 'from_path' from the 'from' connection and upload it to