Dmitry Belyavskiy 0b7faa
diff -up openssh-8.7p1/scp.c.sftpdirs openssh-8.7p1/scp.c
Dmitry Belyavskiy 0b7faa
--- openssh-8.7p1/scp.c.sftpdirs	2022-02-02 14:11:12.553447509 +0100
Dmitry Belyavskiy 0b7faa
+++ openssh-8.7p1/scp.c	2022-02-02 14:12:56.081316414 +0100
Dmitry Belyavskiy 0b7faa
@@ -130,6 +130,7 @@
Dmitry Belyavskiy 0b7faa
 #include "misc.h"
Dmitry Belyavskiy 0b7faa
 #include "progressmeter.h"
Dmitry Belyavskiy 0b7faa
 #include "utf8.h"
Dmitry Belyavskiy 0b7faa
+#include "sftp.h"
Dmitry Belyavskiy 0b7faa
 
Dmitry Belyavskiy 0b7faa
 #include "sftp-common.h"
Dmitry Belyavskiy 0b7faa
 #include "sftp-client.h"
Dmitry Belyavskiy 0b7faa
@@ -660,7 +661,7 @@ main(int argc, char **argv)
Dmitry Belyavskiy 0b7faa
 	 * Finally check the exit status of the ssh process, if one was forked
Dmitry Belyavskiy 0b7faa
 	 * and no error has occurred yet
Dmitry Belyavskiy 0b7faa
 	 */
Dmitry Belyavskiy 0b7faa
-	if (do_cmd_pid != -1 && errs == 0) {
Dmitry Belyavskiy 0b7faa
+	if (do_cmd_pid != -1 && (mode == MODE_SFTP || errs == 0)) {
Dmitry Belyavskiy 0b7faa
 		if (remin != -1)
Dmitry Belyavskiy 0b7faa
 		    (void) close(remin);
Dmitry Belyavskiy 0b7faa
 		if (remout != -1)
Dmitry Belyavskiy 0b7faa
@@ -1264,13 +1265,18 @@ tolocal(int argc, char **argv, enum scp_
Dmitry Belyavskiy 0b7faa
 static char *
Dmitry Belyavskiy 0b7faa
 prepare_remote_path(struct sftp_conn *conn, const char *path)
Dmitry Belyavskiy 0b7faa
 {
Dmitry Belyavskiy 0b7faa
+	size_t nslash;
Dmitry Belyavskiy 0b7faa
+
Dmitry Belyavskiy 0b7faa
 	/* Handle ~ prefixed paths */
Dmitry Belyavskiy 0b7faa
-	if (*path != '~')
Dmitry Belyavskiy 0b7faa
-		return xstrdup(path);
Dmitry Belyavskiy 0b7faa
 	if (*path == '\0' || strcmp(path, "~") == 0)
Dmitry Belyavskiy 0b7faa
 		return xstrdup(".");
Dmitry Belyavskiy 0b7faa
-	if (strncmp(path, "~/", 2) == 0)
Dmitry Belyavskiy 0b7faa
-		return xstrdup(path + 2);
Dmitry Belyavskiy 0b7faa
+	if (*path != '~')
Dmitry Belyavskiy 0b7faa
+		return xstrdup(path);
Dmitry Belyavskiy 0b7faa
+	if (strncmp(path, "~/", 2) == 0) {
Dmitry Belyavskiy 0b7faa
+		if ((nslash = strspn(path + 2, "/")) == strlen(path + 2))
Dmitry Belyavskiy 0b7faa
+			return xstrdup(".");
Dmitry Belyavskiy 0b7faa
+		return xstrdup(path + 2 + nslash);
Dmitry Belyavskiy 0b7faa
+	}
Dmitry Belyavskiy 0b7faa
 	if (can_expand_path(conn))
Dmitry Belyavskiy 0b7faa
 		return do_expand_path(conn, path);
Dmitry Belyavskiy 0b7faa
 	/* No protocol extension */
Dmitry Belyavskiy 0b7faa
@@ -1282,10 +1288,16 @@ void
Dmitry Belyavskiy 0b7faa
 source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn)
Dmitry Belyavskiy 0b7faa
 {
Dmitry Belyavskiy 0b7faa
 	char *target = NULL, *filename = NULL, *abs_dst = NULL;
Dmitry Belyavskiy 0b7faa
-	int target_is_dir;
Dmitry Belyavskiy 0b7faa
-
Dmitry Belyavskiy 0b7faa
+	int src_is_dir, target_is_dir;
Dmitry Belyavskiy 0b7faa
+	Attrib a;
Dmitry Belyavskiy 0b7faa
+	struct stat st;
Dmitry Belyavskiy 0b7faa
+
Dmitry Belyavskiy 0b7faa
+	memset(&a, '\0', sizeof(a));
Dmitry Belyavskiy 0b7faa
+	if (stat(src, &st) != 0)
Dmitry Belyavskiy 0b7faa
+		fatal("stat local \"%s\": %s", src, strerror(errno));
Dmitry Belyavskiy 0b7faa
+	src_is_dir = S_ISDIR(st.st_mode);
Dmitry Belyavskiy 0b7faa
 	if ((filename = basename(src)) == NULL)
Dmitry Belyavskiy 0b7faa
-		fatal("basename %s: %s", src, strerror(errno));
Dmitry Belyavskiy 0b7faa
+		fatal("basename \"%s\": %s", src, strerror(errno));
Dmitry Belyavskiy 0b7faa
 
Dmitry Belyavskiy 0b7faa
 	/*
Dmitry Belyavskiy 0b7faa
 	 * No need to glob here - the local shell already took care of
Dmitry Belyavskiy 0b7faa
@@ -1295,8 +1307,12 @@ source_sftp(int argc, char *src, char *t
Dmitry Belyavskiy 0b7faa
 		cleanup_exit(255);
Dmitry Belyavskiy 0b7faa
 	target_is_dir = remote_is_dir(conn, target);
Dmitry Belyavskiy 0b7faa
 	if (targetshouldbedirectory && !target_is_dir) {
Dmitry Belyavskiy 0b7faa
-		fatal("Target is not a directory, but more files selected "
Dmitry Belyavskiy 0b7faa
-		    "for upload");
Dmitry Belyavskiy 0b7faa
+		debug("target directory \"%s\" does not exist", target);
Dmitry Belyavskiy 0b7faa
+		a.flags = SSH2_FILEXFER_ATTR_PERMISSIONS;
Dmitry Belyavskiy 0b7faa
+		a.perm = st.st_mode | 0700; /* ensure writable */
Dmitry Belyavskiy 0b7faa
+		if (do_mkdir(conn, target, &a, 1) != 0)
Dmitry Belyavskiy 0b7faa
+			cleanup_exit(255); /* error already logged */
Dmitry Belyavskiy 0b7faa
+		target_is_dir = 1;
Dmitry Belyavskiy 0b7faa
 	}
Dmitry Belyavskiy 0b7faa
 	if (target_is_dir)
Dmitry Belyavskiy 0b7faa
 		abs_dst = path_append(target, filename);
Dmitry Belyavskiy 0b7faa
@@ -1306,14 +1322,17 @@ source_sftp(int argc, char *src, char *t
Dmitry Belyavskiy 0b7faa
 	}
Dmitry Belyavskiy 0b7faa
 	debug3_f("copying local %s to remote %s", src, abs_dst);
Dmitry Belyavskiy 0b7faa
 
Dmitry Belyavskiy 0b7faa
-	if (local_is_dir(src) && iamrecursive) {
Dmitry Belyavskiy 0b7faa
+	if (src_is_dir && iamrecursive) {
Dmitry Belyavskiy 0b7faa
 		if (upload_dir(conn, src, abs_dst, pflag,
Dmitry Belyavskiy 0b7faa
 		    SFTP_PROGRESS_ONLY, 0, 0, 1) != 0) {
Dmitry Belyavskiy 0b7faa
-			fatal("failed to upload directory %s to %s",
Dmitry Belyavskiy 0b7faa
+			error("failed to upload directory %s to %s",
Dmitry Belyavskiy 0b7faa
 				src, abs_dst);
Dmitry Belyavskiy 0b7faa
+			errs = 1;
Dmitry Belyavskiy 0b7faa
 		}
Dmitry Belyavskiy 0b7faa
-	} else if (do_upload(conn, src, abs_dst, pflag, 0, 0) != 0)
Dmitry Belyavskiy 0b7faa
-		fatal("failed to upload file %s to %s", src, abs_dst);
Dmitry Belyavskiy 0b7faa
+	} else if (do_upload(conn, src, abs_dst, pflag, 0, 0) != 0) {
Dmitry Belyavskiy 0b7faa
+		error("failed to upload file %s to %s", src, abs_dst);
Dmitry Belyavskiy 0b7faa
+		errs = 1;
Dmitry Belyavskiy 0b7faa
+	}
Dmitry Belyavskiy 0b7faa
 
Dmitry Belyavskiy 0b7faa
 	free(abs_dst);
Dmitry Belyavskiy 0b7faa
 	free(target);
Dmitry Belyavskiy 0b7faa
@@ -1487,14 +1506,15 @@ sink_sftp(int argc, char *dst, const cha
Dmitry Belyavskiy 0b7faa
 	char *abs_dst = NULL;
Dmitry Belyavskiy 0b7faa
 	glob_t g;
Dmitry Belyavskiy 0b7faa
 	char *filename, *tmp = NULL;
Dmitry Belyavskiy 0b7faa
-	int i, r, err = 0;
Dmitry Belyavskiy 0b7faa
+	int i, r, err = 0, dst_is_dir;
Dmitry Belyavskiy 0b7faa
+	struct stat st;
Dmitry Belyavskiy 0b7faa
 
Dmitry Belyavskiy 0b7faa
 	memset(&g, 0, sizeof(g));
Dmitry Belyavskiy 0b7faa
+
Dmitry Belyavskiy 0b7faa
 	/*
Dmitry Belyavskiy 0b7faa
 	 * Here, we need remote glob as SFTP can not depend on remote shell
Dmitry Belyavskiy 0b7faa
 	 * expansions
Dmitry Belyavskiy 0b7faa
 	 */
Dmitry Belyavskiy 0b7faa
-
Dmitry Belyavskiy 0b7faa
 	if ((abs_src = prepare_remote_path(conn, src)) == NULL) {
Dmitry Belyavskiy 0b7faa
 		err = -1;
Dmitry Belyavskiy 0b7faa
 		goto out;
Dmitry Belyavskiy 0b7faa
@@ -1510,11 +1530,24 @@ sink_sftp(int argc, char *dst, const cha
Dmitry Belyavskiy 0b7faa
 		goto out;
Dmitry Belyavskiy 0b7faa
 	}
Dmitry Belyavskiy 0b7faa
 
Dmitry Belyavskiy 0b7faa
-	if (g.gl_matchc > 1 && !local_is_dir(dst)) {
Dmitry Belyavskiy 0b7faa
-		error("Multiple files match pattern, but destination "
Dmitry Belyavskiy 0b7faa
-		    "\"%s\" is not a directory", dst);
Dmitry Belyavskiy 0b7faa
-		err = -1;
Dmitry Belyavskiy 0b7faa
-		goto out;
Dmitry Belyavskiy 0b7faa
+	if ((r = stat(dst, &st)) != 0)
Dmitry Belyavskiy 0b7faa
+		debug2_f("stat local \"%s\": %s", dst, strerror(errno));
Dmitry Belyavskiy 0b7faa
+	dst_is_dir = r == 0 && S_ISDIR(st.st_mode);
Dmitry Belyavskiy 0b7faa
+
Dmitry Belyavskiy 0b7faa
+	if (g.gl_matchc > 1 && !dst_is_dir) {
Dmitry Belyavskiy 0b7faa
+		if (r == 0) {
Dmitry Belyavskiy 0b7faa
+			error("Multiple files match pattern, but destination "
Dmitry Belyavskiy 0b7faa
+			    "\"%s\" is not a directory", dst);
Dmitry Belyavskiy 0b7faa
+			err = -1;
Dmitry Belyavskiy 0b7faa
+			goto out;
Dmitry Belyavskiy 0b7faa
+		}
Dmitry Belyavskiy 0b7faa
+		debug2_f("creating destination \"%s\"", dst);
Dmitry Belyavskiy 0b7faa
+		if (mkdir(dst, 0777) != 0) {
Dmitry Belyavskiy 0b7faa
+			error("local mkdir \"%s\": %s", dst, strerror(errno));
Dmitry Belyavskiy 0b7faa
+			err = -1;
Dmitry Belyavskiy 0b7faa
+			goto out;
Dmitry Belyavskiy 0b7faa
+		}
Dmitry Belyavskiy 0b7faa
+		dst_is_dir = 1;
Dmitry Belyavskiy 0b7faa
 	}
Dmitry Belyavskiy 0b7faa
 
Dmitry Belyavskiy 0b7faa
 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Dmitry Belyavskiy 0b7faa
@@ -1525,7 +1558,7 @@ sink_sftp(int argc, char *dst, const cha
Dmitry Belyavskiy 0b7faa
 			goto out;
Dmitry Belyavskiy 0b7faa
 		}
Dmitry Belyavskiy 0b7faa
 
Dmitry Belyavskiy 0b7faa
-		if (local_is_dir(dst))
Dmitry Belyavskiy 0b7faa
+		if (dst_is_dir)
Dmitry Belyavskiy 0b7faa
 			abs_dst = path_append(dst, filename);
Dmitry Belyavskiy 0b7faa
 		else
Dmitry Belyavskiy 0b7faa
 			abs_dst = xstrdup(dst);
Dmitry Belyavskiy 0b7faa
@@ -1551,7 +1584,8 @@ out:
Dmitry Belyavskiy 0b7faa
 	free(tmp);
Dmitry Belyavskiy 0b7faa
 	globfree(&g);
Dmitry Belyavskiy 0b7faa
 	if (err == -1) {
Dmitry Belyavskiy 0b7faa
-		fatal("Failed to download file '%s'", src);
Dmitry Belyavskiy 0b7faa
+		error("Failed to download '%s'", src);
Dmitry Belyavskiy 0b7faa
+		errs = 1;
Dmitry Belyavskiy 0b7faa
 	}
Dmitry Belyavskiy 0b7faa
 }
Dmitry Belyavskiy 0b7faa