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