- djm@cvs.openbsd.org 2009/08/18 18:36:21

[sftp-client.h sftp.1 sftp-client.c sftp.c]
     recursive transfer support for get/put and on the commandline
     work mostly by carlosvsilvapt@gmail.com for the Google Summer of Code
     with some tweaks by me; "go for it" deraadt@
This commit is contained in:
Darren Tucker 2009-10-07 08:37:48 +11:00
parent 1477ea162c
commit 1b0dd17537
5 changed files with 423 additions and 126 deletions

View File

@ -32,6 +32,11 @@
- dtucker@cvs.openbsd.org 2009/08/16 23:29:26
[sshd_config.5]
Add PubkeyAuthentication to the list allowed in a Match block (bz #1577)
- djm@cvs.openbsd.org 2009/08/18 18:36:21
[sftp-client.h sftp.1 sftp-client.c sftp.c]
recursive transfer support for get/put and on the commandline
work mostly by carlosvsilvapt@gmail.com for the Google Summer of Code
with some tweaks by me; "go for it" deraadt@
20091002
- (djm) [Makefile.in] Mention readconf.o in ssh-keysign's make deps.

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-client.c,v 1.88 2009/08/14 18:17:49 djm Exp $ */
/* $OpenBSD: sftp-client.c,v 1.89 2009/08/18 18:36:20 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
@ -36,6 +36,7 @@
#endif
#include <sys/uio.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
@ -61,6 +62,9 @@ extern int showprogress;
/* Minimum amount of data to read at a time */
#define MIN_READ_SIZE 512
/* Maximum depth to descend in directory trees */
#define MAX_DIR_DEPTH 64
struct sftp_conn {
int fd_in;
int fd_out;
@ -497,6 +501,17 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
if (printflag)
printf("%s\n", longname);
/*
* Directory entries should never contain '/'
* These can be used to attack recursive ops
* (e.g. send '../../../../etc/passwd')
*/
if (strchr(filename, '/') != NULL) {
error("Server sent suspect path \"%s\" "
"during readdir of \"%s\"", filename, path);
goto next;
}
if (dir) {
*dir = xrealloc(*dir, ents + 2, sizeof(**dir));
(*dir)[ents] = xmalloc(sizeof(***dir));
@ -505,7 +520,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
memcpy(&(*dir)[ents]->a, a, sizeof(*a));
(*dir)[++ents] = NULL;
}
next:
xfree(filename);
xfree(longname);
}
@ -560,7 +575,7 @@ do_rm(struct sftp_conn *conn, char *path)
}
int
do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
{
u_int status, id;
@ -569,7 +584,7 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
strlen(path), a);
status = get_status(conn->fd_in, id);
if (status != SSH2_FX_OK)
if (status != SSH2_FX_OK && printflag)
error("Couldn't create directory: %s", fx2txt(status));
return(status);
@ -908,9 +923,9 @@ send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
int
do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
int pflag)
Attrib *a, int pflag)
{
Attrib junk, *a;
Attrib junk;
Buffer msg;
char *handle;
int local_fd, status = 0, write_error;
@ -929,9 +944,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
TAILQ_INIT(&requests);
a = do_stat(conn, remote_path, 0);
if (a == NULL)
return(-1);
if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
return -1;
/* Do not preserve set[ug]id here, as we do not preserve ownership */
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
@ -1146,6 +1160,114 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
return(status);
}
static int
download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
Attrib *dirattrib, int pflag, int printflag, int depth)
{
int i, ret = 0;
SFTP_DIRENT **dir_entries;
char *filename, *new_src, *new_dst;
mode_t mode = 0777;
if (depth >= MAX_DIR_DEPTH) {
error("Maximum directory depth exceeded: %d levels", depth);
return -1;
}
if (dirattrib == NULL &&
(dirattrib = do_stat(conn, src, 1)) == NULL) {
error("Unable to stat remote directory \"%s\"", src);
return -1;
}
if (!S_ISDIR(dirattrib->perm)) {
error("\"%s\" is not a directory", src);
return -1;
}
if (printflag)
printf("Retrieving %s\n", src);
if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
mode = dirattrib->perm & 01777;
else {
debug("Server did not send permissions for "
"directory \"%s\"", dst);
}
if (mkdir(dst, mode) == -1 && errno != EEXIST) {
error("mkdir %s: %s", dst, strerror(errno));
return -1;
}
if (do_readdir(conn, src, &dir_entries) == -1) {
error("%s: Failed to get directory contents", src);
return -1;
}
for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
filename = dir_entries[i]->filename;
new_dst = path_append(dst, filename);
new_src = path_append(src, filename);
if (S_ISDIR(dir_entries[i]->a.perm)) {
if (strcmp(filename, ".") == 0 ||
strcmp(filename, "..") == 0)
continue;
if (download_dir_internal(conn, new_src, new_dst,
&(dir_entries[i]->a), pflag, printflag,
depth + 1) == -1)
ret = -1;
} else if (S_ISREG(dir_entries[i]->a.perm) ) {
if (do_download(conn, new_src, new_dst,
&(dir_entries[i]->a), pflag) == -1) {
error("Download of file %s to %s failed",
new_src, new_dst);
ret = -1;
}
} else
logit("%s: not a regular file\n", new_src);
xfree(new_dst);
xfree(new_src);
}
if (pflag) {
if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
struct timeval tv[2];
tv[0].tv_sec = dirattrib->atime;
tv[1].tv_sec = dirattrib->mtime;
tv[0].tv_usec = tv[1].tv_usec = 0;
if (utimes(dst, tv) == -1)
error("Can't set times on \"%s\": %s",
dst, strerror(errno));
} else
debug("Server did not send times for directory "
"\"%s\"", dst);
}
free_sftp_dirents(dir_entries);
return ret;
}
int
download_dir(struct sftp_conn *conn, char *src, char *dst,
Attrib *dirattrib, int pflag, int printflag)
{
char *src_canon;
int ret;
if ((src_canon = do_realpath(conn, src)) == NULL) {
error("Unable to canonicalise path \"%s\"", src);
return -1;
}
ret = download_dir_internal(conn, src_canon, dst,
dirattrib, pflag, printflag, 0);
xfree(src_canon);
return ret;
}
int
do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
int pflag)
@ -1328,3 +1450,123 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
return status;
}
static int
upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
int pflag, int printflag, int depth)
{
int ret = 0, status;
DIR *dirp;
struct dirent *dp;
char *filename, *new_src, *new_dst;
struct stat sb;
Attrib a;
if (depth >= MAX_DIR_DEPTH) {
error("Maximum directory depth exceeded: %d levels", depth);
return -1;
}
if (stat(src, &sb) == -1) {
error("Couldn't stat directory \"%s\": %s",
src, strerror(errno));
return -1;
}
if (!S_ISDIR(sb.st_mode)) {
error("\"%s\" is not a directory", src);
return -1;
}
if (printflag)
printf("Entering %s\n", src);
attrib_clear(&a);
stat_to_attrib(&sb, &a);
a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
a.perm &= 01777;
if (!pflag)
a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
status = do_mkdir(conn, dst, &a, 0);
/*
* we lack a portable status for errno EEXIST,
* so if we get a SSH2_FX_FAILURE back we must check
* if it was created successfully.
*/
if (status != SSH2_FX_OK) {
if (status != SSH2_FX_FAILURE)
return -1;
if (do_stat(conn, dst, 0) == NULL)
return -1;
}
if ((dirp = opendir(src)) == NULL) {
error("Failed to open dir \"%s\": %s", src, strerror(errno));
return -1;
}
while (((dp = readdir(dirp)) != NULL) && !interrupted) {
if (dp->d_ino == 0)
continue;
filename = dp->d_name;
new_dst = path_append(dst, filename);
new_src = path_append(src, filename);
if (S_ISDIR(DTTOIF(dp->d_type))) {
if (strcmp(filename, ".") == 0 ||
strcmp(filename, "..") == 0)
continue;
if (upload_dir_internal(conn, new_src, new_dst,
pflag, depth + 1, printflag) == -1)
ret = -1;
} else if (S_ISREG(DTTOIF(dp->d_type)) ) {
if (do_upload(conn, new_src, new_dst, pflag) == -1) {
error("Uploading of file %s to %s failed!",
new_src, new_dst);
ret = -1;
}
} else
logit("%s: not a regular file\n", filename);
xfree(new_dst);
xfree(new_src);
}
do_setstat(conn, dst, &a);
(void) closedir(dirp);
return ret;
}
int
upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
int pflag)
{
char *dst_canon;
int ret;
if ((dst_canon = do_realpath(conn, dst)) == NULL) {
error("Unable to canonicalise path \"%s\"", dst);
return -1;
}
ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
xfree(dst_canon);
return ret;
}
char *
path_append(char *p1, char *p2)
{
char *ret;
size_t len = strlen(p1) + strlen(p2) + 2;
ret = xmalloc(len);
strlcpy(ret, p1, len);
if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
strlcat(ret, "/", len);
strlcat(ret, p2, len);
return(ret);
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-client.h,v 1.17 2008/06/08 20:15:29 dtucker Exp $ */
/* $OpenBSD: sftp-client.h,v 1.18 2009/08/18 18:36:20 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@ -68,7 +68,7 @@ void free_sftp_dirents(SFTP_DIRENT **);
int do_rm(struct sftp_conn *, char *);
/* Create directory 'path' */
int do_mkdir(struct sftp_conn *, char *, Attrib *);
int do_mkdir(struct sftp_conn *, char *, Attrib *, int);
/* Remove directory 'path' */
int do_rmdir(struct sftp_conn *, char *);
@ -103,7 +103,13 @@ int do_symlink(struct sftp_conn *, char *, char *);
* Download 'remote_path' to 'local_path'. Preserve permissions and times
* if 'pflag' is set
*/
int do_download(struct sftp_conn *, char *, char *, int);
int do_download(struct sftp_conn *, char *, char *, Attrib *, int);
/*
* Recursively download 'remote_directory' to 'local_directory'. Preserve
* times if 'pflag' is set
*/
int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int);
/*
* Upload 'local_path' to 'remote_path'. Preserve permissions and times
@ -111,4 +117,13 @@ int do_download(struct sftp_conn *, char *, char *, int);
*/
int do_upload(struct sftp_conn *, char *, char *, int);
/*
* Recursively upload 'local_directory' to 'remote_directory'. Preserve
* times if 'pflag' is set
*/
int upload_dir(struct sftp_conn *, char *, char *, int, int);
/* Concatenate paths, taking care of slashes. Caller must free result. */
char *path_append(char *, char *);
#endif

44
sftp.1
View File

@ -1,4 +1,4 @@
.\" $OpenBSD: sftp.1,v 1.73 2009/08/13 13:39:54 jmc Exp $
.\" $OpenBSD: sftp.1,v 1.74 2009/08/18 18:36:20 djm Exp $
.\"
.\" Copyright (c) 2001 Damien Miller. All rights reserved.
.\"
@ -22,7 +22,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd $Mdocdate: August 13 2009 $
.Dd $Mdocdate: August 18 2009 $
.Dt SFTP 1
.Os
.Sh NAME
@ -31,7 +31,7 @@
.Sh SYNOPSIS
.Nm sftp
.Bk -words
.Op Fl 1246Cqv
.Op Fl 1246Cpqrv
.Op Fl B Ar buffer_size
.Op Fl b Ar batchfile
.Op Fl c Ar cipher
@ -223,6 +223,9 @@ For full details of the options listed below, and their possible values, see
.El
.It Fl P Ar port
Specifies the port to connect to on the remote host.
.It Fl p
Preserves modification times, access times, and modes from the
original files transferred.
.It Fl q
Quiet mode: disables the progress meter as well as warning and
diagnostic messages from
@ -232,6 +235,11 @@ Specify how many requests may be outstanding at any one time.
Increasing this may slightly improve file transfer speed
but will increase memory usage.
The default is 64 outstanding requests.
.It Fl r
Recursively copy entire directories when uploading and downloading.
Note that
.Nm
does not follow symbolic links encountered in the tree traversal.
.It Fl S Ar program
Name of the
.Ar program
@ -322,7 +330,7 @@ extension.
Quit
.Nm sftp .
.It Xo Ic get
.Op Fl P
.Op Fl Ppr
.Ar remote-path
.Op Ar local-path
.Xc
@ -341,10 +349,20 @@ If it does and
is specified, then
.Ar local-path
must specify a directory.
If the
.Fl P
.Pp
If ether the
.Fl Ppr
or
.Fl p
flag is specified, then full file permissions and access times are
copied too.
.Pp
If the
.Fl r
flag is specified then directories will be copied recursively.
Note that
.Nm
does not follow symbolic links when performing recursive transfers.
.It Ic help
Display help text.
.It Ic lcd Ar path
@ -440,10 +458,20 @@ If it does and
is specified, then
.Ar remote-path
must specify a directory.
If the
.Pp
If ether the
.Fl P
flag is specified, then the file's full permission and access time are
or
.Fl p
flag is specified, then full file permissions and access times are
copied too.
.Pp
If the
.Fl r
flag is specified then directories will be copied recursively.
Note that
.Nm
does not follow symbolic links when performing recursive transfers.
.It Ic pwd
Display remote working directory.
.It Ic quit

219
sftp.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp.c,v 1.110 2009/08/13 13:39:54 jmc Exp $ */
/* $OpenBSD: sftp.c,v 1.111 2009/08/18 18:36:21 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
@ -35,6 +35,9 @@
#ifdef HAVE_PATHS_H
# include <paths.h>
#endif
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
#ifdef USE_LIBEDIT
#include <histedit.h>
#else
@ -83,6 +86,12 @@ static pid_t sshpid = -1;
/* This is set to 0 if the progressmeter is not desired. */
int showprogress = 1;
/* When this option is set, we always recursively download/upload directories */
int global_rflag = 0;
/* When this option is set, the file transfers will always preserve times */
int global_pflag = 0;
/* SIGINT received during command processing */
volatile sig_atomic_t interrupted = 0;
@ -216,7 +225,7 @@ help(void)
"df [-hi] [path] Display statistics for current directory or\n"
" filesystem containing 'path'\n"
"exit Quit sftp\n"
"get [-P] remote-path [local-path] Download file\n"
"get [-Pr] remote-path [local-path] Download file\n"
"help Display this help text\n"
"lcd path Change local directory to 'path'\n"
"lls [ls-options [path]] Display local directory listing\n"
@ -227,7 +236,7 @@ help(void)
"lumask umask Set local umask to 'umask'\n"
"mkdir path Create remote directory\n"
"progress Toggle display of progress meter\n"
"put [-P] local-path [remote-path] Upload file\n"
"put [-Pr] local-path [remote-path] Upload file\n"
"pwd Display remote working directory\n"
"quit Quit sftp\n"
"rename oldpath newpath Rename remote file\n"
@ -313,21 +322,6 @@ path_strip(char *path, char *strip)
return (xstrdup(path));
}
static char *
path_append(char *p1, char *p2)
{
char *ret;
size_t len = strlen(p1) + strlen(p2) + 2;
ret = xmalloc(len);
strlcpy(ret, p1, len);
if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
strlcat(ret, "/", len);
strlcat(ret, p2, len);
return(ret);
}
static char *
make_absolute(char *p, char *pwd)
{
@ -343,27 +337,8 @@ make_absolute(char *p, char *pwd)
}
static int
infer_path(const char *p, char **ifp)
{
char *cp;
cp = strrchr(p, '/');
if (cp == NULL) {
*ifp = xstrdup(p);
return(0);
}
if (!cp[1]) {
error("Invalid path");
return(-1);
}
*ifp = xstrdup(cp + 1);
return(0);
}
static int
parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
int *rflag)
{
extern int opterr, optind, optopt, optreset;
int ch;
@ -371,13 +346,17 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
optind = optreset = 1;
opterr = 0;
*pflag = 0;
while ((ch = getopt(argc, argv, "Pp")) != -1) {
*rflag = *pflag = 0;
while ((ch = getopt(argc, argv, "PpRr")) != -1) {
switch (ch) {
case 'p':
case 'P':
*pflag = 1;
break;
case 'r':
case 'R':
*rflag = 1;
break;
default:
error("%s: Invalid flag -%c", cmd, optopt);
return -1;
@ -489,62 +468,79 @@ remote_is_dir(struct sftp_conn *conn, char *path)
return(S_ISDIR(a->perm));
}
/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
static int
process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
pathname_is_dir(char *pathname)
{
size_t l = strlen(pathname);
return l > 0 && pathname[l - 1] == '/';
}
static int
process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
int pflag, int rflag)
{
char *abs_src = NULL;
char *abs_dst = NULL;
char *tmp;
glob_t g;
int err = 0;
int i;
char *filename, *tmp=NULL;
int i, err = 0;
abs_src = xstrdup(src);
abs_src = make_absolute(abs_src, pwd);
memset(&g, 0, sizeof(g));
debug3("Looking up %s", abs_src);
if (remote_glob(conn, abs_src, 0, NULL, &g)) {
if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
error("File \"%s\" not found.", abs_src);
err = -1;
goto out;
}
/* If multiple matches, dst must be a directory or unspecified */
if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
error("Multiple files match, but \"%s\" is not a directory",
dst);
/*
* If multiple matches then dst must be a directory or
* unspecified.
*/
if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
error("Multiple source paths, but destination "
"\"%s\" is not a directory", dst);
err = -1;
goto out;
}
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
if (infer_path(g.gl_pathv[i], &tmp)) {
tmp = xstrdup(g.gl_pathv[i]);
if ((filename = basename(tmp)) == NULL) {
error("basename %s: %s", tmp, strerror(errno));
xfree(tmp);
err = -1;
goto out;
}
if (g.gl_matchc == 1 && dst) {
/* If directory specified, append filename */
xfree(tmp);
if (is_dir(dst)) {
if (infer_path(g.gl_pathv[0], &tmp)) {
err = 1;
goto out;
}
abs_dst = path_append(dst, tmp);
xfree(tmp);
} else
abs_dst = path_append(dst, filename);
} else {
abs_dst = xstrdup(dst);
}
} else if (dst) {
abs_dst = path_append(dst, tmp);
xfree(tmp);
} else
abs_dst = tmp;
abs_dst = path_append(dst, filename);
} else {
abs_dst = xstrdup(filename);
}
xfree(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)
err = -1;
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
pflag || global_pflag, 1) == -1)
err = -1;
} else {
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
pflag || global_pflag) == -1)
err = -1;
}
xfree(abs_dst);
abs_dst = NULL;
}
@ -556,14 +552,15 @@ out:
}
static int
process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
int pflag, int rflag)
{
char *tmp_dst = NULL;
char *abs_dst = NULL;
char *tmp;
char *tmp = NULL, *filename = NULL;
glob_t g;
int err = 0;
int i;
int i, dst_is_dir = 1;
struct stat sb;
if (dst) {
@ -573,16 +570,20 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
memset(&g, 0, sizeof(g));
debug3("Looking up %s", src);
if (glob(src, GLOB_NOCHECK, NULL, &g)) {
if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
error("File \"%s\" not found.", src);
err = -1;
goto out;
}
/* If we aren't fetching to pwd then stash this status for later */
if (tmp_dst != NULL)
dst_is_dir = remote_is_dir(conn, tmp_dst);
/* If multiple matches, dst may be directory or unspecified */
if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
error("Multiple files match, but \"%s\" is not a directory",
tmp_dst);
if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
error("Multiple paths match, but destination "
"\"%s\" is not a directory", tmp_dst);
err = -1;
goto out;
}
@ -593,38 +594,38 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
error("stat %s: %s", g.gl_pathv[i], strerror(errno));
continue;
}
if (!S_ISREG(sb.st_mode)) {
error("skipping non-regular file %s",
g.gl_pathv[i]);
continue;
}
if (infer_path(g.gl_pathv[i], &tmp)) {
tmp = xstrdup(g.gl_pathv[i]);
if ((filename = basename(tmp)) == NULL) {
error("basename %s: %s", tmp, strerror(errno));
xfree(tmp);
err = -1;
goto out;
}
if (g.gl_matchc == 1 && tmp_dst) {
/* If directory specified, append filename */
if (remote_is_dir(conn, tmp_dst)) {
if (infer_path(g.gl_pathv[0], &tmp)) {
err = 1;
goto out;
}
abs_dst = path_append(tmp_dst, tmp);
xfree(tmp);
} else
if (dst_is_dir)
abs_dst = path_append(tmp_dst, filename);
else
abs_dst = xstrdup(tmp_dst);
} else if (tmp_dst) {
abs_dst = path_append(tmp_dst, tmp);
xfree(tmp);
} else
abs_dst = make_absolute(tmp, pwd);
abs_dst = path_append(tmp_dst, filename);
} else {
abs_dst = make_absolute(xstrdup(filename), pwd);
}
xfree(tmp);
printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
err = -1;
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
if (upload_dir(conn, g.gl_pathv[i], abs_dst,
pflag || global_pflag, 1) == -1)
err = -1;
} else {
if (do_upload(conn, g.gl_pathv[i], abs_dst,
pflag || global_pflag) == -1)
err = -1;
}
}
out:
@ -1065,7 +1066,7 @@ makeargv(const char *arg, int *argcp)
}
static int
parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag,
unsigned long *n_arg, char **path1, char **path2)
{
const char *cmd, *cp = *cpp;
@ -1109,13 +1110,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
}
/* Get arguments and parse flags */
*lflag = *pflag = *hflag = *n_arg = 0;
*lflag = *pflag = *rflag = *hflag = *n_arg = 0;
*path1 = *path2 = NULL;
optidx = 1;
switch (cmdnum) {
case I_GET:
case I_PUT:
if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
return -1;
/* Get first pathname (mandatory) */
if (argc - optidx < 1) {
@ -1235,7 +1236,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
int err_abort)
{
char *path1, *path2, *tmp;
int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
unsigned long n_arg = 0;
Attrib a, *aa;
char path_buf[MAXPATHLEN];
@ -1243,7 +1244,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
glob_t g;
path1 = path2 = NULL;
cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
&path1, &path2);
if (iflag != 0)
@ -1261,10 +1262,10 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
err = -1;
break;
case I_GET:
err = process_get(conn, path1, path2, *pwd, pflag);
err = process_get(conn, path1, path2, *pwd, pflag, rflag);
break;
case I_PUT:
err = process_put(conn, path1, path2, *pwd, pflag);
err = process_put(conn, path1, path2, *pwd, pflag, rflag);
break;
case I_RENAME:
path1 = make_absolute(path1, *pwd);
@ -1290,7 +1291,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
attrib_clear(&a);
a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
a.perm = 0777;
err = do_mkdir(conn, path1, &a);
err = do_mkdir(conn, path1, &a, 1);
break;
case I_RMDIR:
path1 = make_absolute(path1, *pwd);
@ -1668,7 +1669,7 @@ usage(void)
extern char *__progname;
fprintf(stderr,
"usage: %s [-1246Cqv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
"usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
" [-D sftp_server_path] [-F ssh_config] "
"[-i identity_file]\n"
" [-o ssh_option] [-P port] [-R num_requests] "
@ -1710,7 +1711,7 @@ main(int argc, char **argv)
infile = stdin;
while ((ch = getopt(argc, argv,
"1246hqvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
"1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
switch (ch) {
/* Passed through to ssh(1) */
case '4':
@ -1764,9 +1765,15 @@ main(int argc, char **argv)
batchmode = 1;
addargs(&args, "-obatchmode yes");
break;
case 'p':
global_pflag = 1;
break;
case 'D':
sftp_direct = optarg;
break;
case 'r':
global_rflag = 1;
break;
case 'R':
num_requests = strtol(optarg, &cp, 10);
if (num_requests == 0 || *cp != '\0')