3.7.1p2 sftp recurse patch

Jared Yanovich jjy2+ at pitt.edu
Wed Oct 1 16:05:18 EST 2003


This patch is against OpenSSH 3.7.1p2 sources. It adds recursive
(directory) downloading and uploading. Criticism/suggestions welcome.

I would imagine the time official support is added, recursive operations
will be handled on a per-command basis as a flag as opposed to a global
toggle command (such as get -r)?

diff -ru openssh-3.7.1p2/sftp-int.c openssh-3.7.1p2-patched/sftp-int.c
--- openssh-3.7.1p2/sftp-int.c	Tue Sep 23 05:24:21 2003
+++ openssh-3.7.1p2-patched/sftp-int.c	Thu Sep 25 16:56:13 2003
@@ -50,6 +50,9 @@
 /* This is set to 0 if the progressmeter is not desired. */
 int showprogress = 1;
 
+/* Recursive operations */
+int recursion = 0;
+
 /* Seperators for interactive commands */
 #define WHITESPACE " \t\r\n"
 
@@ -81,6 +84,7 @@
 #define I_SYMLINK	21
 #define I_VERSION	22
 #define I_PROGRESS	23
+#define I_RECURSE	24
 
 struct CMD {
 	const char *c;
@@ -113,6 +117,7 @@
 	{ "mput",	I_PUT },
 	{ "pwd",	I_PWD },
 	{ "quit",	I_QUIT },
+	{ "recurse",	I_RECURSE },
 	{ "rename",	I_RENAME },
 	{ "rm",		I_RM },
 	{ "rmdir",	I_RMDIR },
@@ -147,6 +152,7 @@
 	printf("exit                          Quit sftp\n");
 	printf("quit                          Quit sftp\n");
 	printf("rename oldpath newpath        Rename remote file\n");
+	printf("recurse                       Toggle recursive operations\n");
 	printf("rmdir path                    Remove remote directory\n");
 	printf("rm path                       Delete remote file\n");
 	printf("symlink oldpath newpath       Symlink remote file\n");
@@ -430,6 +436,106 @@
 }
 
 static int
+do_recursive_download(struct sftp_conn *conn, char *remote_path,
+		      char *local_path, int pflag)
+{
+	char *remote_tmp, *local_tmp;
+	int err, n;
+	SFTP_DIRENT **d;
+	extern int errno;
+
+	if (recursion && remote_is_dir(conn, remote_path)) {
+		if (!is_dir(local_path)) {
+			/* Create local directory */
+			err = mkdir(local_path, 0777);
+			if (err == -1) {
+				error("Couldn't create local directory \"%s\": "
+					"%s", local_path, strerror(errno));
+				goto END;
+			}
+		}
+		
+		err = do_readdir(conn, remote_path, &d);
+		if (err == -1) {
+			error("Couldn't gather list of remote files");
+			goto END;
+		}
+
+		for (n = 0; d[n] != NULL; n++) {
+			/* Skip '.' and '..' */
+			if ((strcmp(d[n]->filename, ".")  == 0) ||
+			    (strcmp(d[n]->filename, "..") == 0))
+				continue;
+			remote_tmp = path_append(remote_path, d[n]->filename);
+			local_tmp = path_append(local_path, d[n]->filename);
+			err = do_recursive_download(conn, remote_tmp,
+				local_tmp, pflag);
+			xfree(remote_tmp);
+			xfree(local_tmp);
+			if (err == -1)
+				break;
+		}
+		free_sftp_dirents(d);
+	} else
+		err = do_download(conn, remote_path, local_path, pflag);
+END:
+	return err;
+}
+
+static int
+do_recursive_upload(struct sftp_conn *conn, char *local_path,
+		    char *remote_path, int pflag)
+{
+	int err;
+	DIR *d;
+	struct dirent *f;
+	char *local_tmp, *remote_tmp;
+	Attrib a;
+	extern int errno;
+
+	if (recursion && is_dir(local_path)) {
+		if (!remote_is_dir(conn, remote_path)) {
+			/* Create remote directory */
+			attrib_clear(&a);
+			a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
+			a.perm = 0777;
+			err = do_mkdir(conn, remote_path, &a);
+			if (err == -1) {
+				error("Couldn't create remote directory \"%s\"",
+					remote_path);
+				goto END;
+			}
+		}
+
+		d = opendir(local_path);
+		if (d == NULL) {
+			error("Unable to read local directory \"%s\": %s",
+				local_path, strerror(errno));
+			err = -1;
+			goto END;
+		}
+		while ((f = readdir(d)) != NULL) {
+			/* Skip '.' and '..' */
+			if ((strcmp(f->d_name, ".")  == 0) ||
+			    (strcmp(f->d_name, "..") == 0))
+				continue;
+			local_tmp = path_append(local_path, f->d_name);
+			remote_tmp = path_append(remote_path, f->d_name);
+			err = do_recursive_upload(conn, local_tmp, remote_tmp,
+				pflag);
+			xfree(remote_tmp);
+			xfree(local_tmp);
+			if (err == -1)
+				break;
+		}
+		closedir(d);
+	} else
+		err = do_upload(conn, local_path, remote_path, pflag);
+END:
+	return err;
+}
+
+static int
 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
 {
 	char *abs_src = NULL;
@@ -482,7 +588,7 @@
 			abs_dst = tmp;
 
 		printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
-		if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
+		if (do_recursive_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
 			err = -1;
 		xfree(abs_dst);
 		abs_dst = NULL;
@@ -528,7 +634,7 @@
 	}
 
 	for (i = 0; g.gl_pathv[i]; i++) {
-		if (!is_reg(g.gl_pathv[i])) {
+		if (!is_reg(g.gl_pathv[i]) && !(recursion && is_dir(g.gl_pathv[i]))) {
 			error("skipping non-regular file %s", 
 			    g.gl_pathv[i]);
 			continue;
@@ -557,7 +663,8 @@
 			abs_dst = make_absolute(tmp, pwd);
 
 		printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
-		if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
+		if (do_recursive_upload(conn, g.gl_pathv[i], abs_dst,
+		    pflag) == -1)
 			err = -1;
 	}
 
@@ -881,6 +988,7 @@
 	case I_HELP:
 	case I_VERSION:
 	case I_PROGRESS:
+	case I_RECURSE:
 		break;
 	default:
 		fatal("Command not implemented");
@@ -1093,6 +1201,13 @@
 		else
 			printf("Progress meter disabled\n");
 		break;
+	case I_RECURSE:
+		recursion = !recursion;
+		if (recursion)
+			printf("Recursive operations enabled\n");
+		else
+			printf("Recursive operations disabled\n");
+		break;
 	default:
 		fatal("%d is not implemented", cmdnum);
 	}
diff -ru openssh-3.7.1p2/sftp.1 openssh-3.7.1p2-patched/sftp.1
--- openssh-3.7.1p2/sftp.1	Tue Sep  2 22:13:30 2003
+++ openssh-3.7.1p2-patched/sftp.1	Thu Sep 25 17:00:30 2003
@@ -256,6 +256,8 @@
 .Ar path .
 .It Ic progress
 Toggle display of progress meter.
+.It Ic recurse
+Toggle recursive operations.
 .It Xo Ic put
 .Op Ar flags
 .Ar local-path

	- Jared




More information about the openssh-unix-dev mailing list