mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-07-31 01:35:11 +02:00
- djm@cvs.openbsd.org 2013/07/25 00:56:52
[sftp-client.c sftp-client.h sftp.1 sftp.c] sftp support for resuming partial downloads; patch mostly by Loganaden Velvindron/AfriNIC with some tweaks by me; feedback and ok dtucker@
This commit is contained in:
parent
98e27dcf58
commit
0d032419ee
@ -15,6 +15,10 @@
|
|||||||
[ssh.c]
|
[ssh.c]
|
||||||
daemonise backgrounded (ControlPersist'ed) multiplexing master to ensure
|
daemonise backgrounded (ControlPersist'ed) multiplexing master to ensure
|
||||||
it is fully detached from its controlling terminal. based on debugging
|
it is fully detached from its controlling terminal. based on debugging
|
||||||
|
- djm@cvs.openbsd.org 2013/07/25 00:56:52
|
||||||
|
[sftp-client.c sftp-client.h sftp.1 sftp.c]
|
||||||
|
sftp support for resuming partial downloads; patch mostly by Loganaden
|
||||||
|
Velvindron/AfriNIC with some tweaks by me; feedback and ok dtucker@
|
||||||
|
|
||||||
20130720
|
20130720
|
||||||
- (djm) OpenBSD CVS Sync
|
- (djm) OpenBSD CVS Sync
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: sftp-client.c,v 1.100 2013/06/01 22:34:50 dtucker Exp $ */
|
/* $OpenBSD: sftp-client.c,v 1.101 2013/07/25 00:56:51 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||||
*
|
*
|
||||||
@ -112,7 +112,7 @@ send_msg(struct sftp_conn *conn, Buffer *m)
|
|||||||
iov[1].iov_len = buffer_len(m);
|
iov[1].iov_len = buffer_len(m);
|
||||||
|
|
||||||
if (atomiciov6(writev, conn->fd_out, iov, 2,
|
if (atomiciov6(writev, conn->fd_out, iov, 2,
|
||||||
conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
|
conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
|
||||||
buffer_len(m) + sizeof(mlen))
|
buffer_len(m) + sizeof(mlen))
|
||||||
fatal("Couldn't send packet: %s", strerror(errno));
|
fatal("Couldn't send packet: %s", strerror(errno));
|
||||||
|
|
||||||
@ -988,16 +988,17 @@ send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
|
|||||||
|
|
||||||
int
|
int
|
||||||
do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
|
do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
|
||||||
Attrib *a, int pflag)
|
Attrib *a, int pflag, int resume)
|
||||||
{
|
{
|
||||||
Attrib junk;
|
Attrib junk;
|
||||||
Buffer msg;
|
Buffer msg;
|
||||||
char *handle;
|
char *handle;
|
||||||
int local_fd, status = 0, write_error;
|
int local_fd = -1, status = 0, write_error;
|
||||||
int read_error, write_errno;
|
int read_error, write_errno, reordered = 0;
|
||||||
u_int64_t offset, size;
|
u_int64_t offset = 0, size, highwater;
|
||||||
u_int handle_len, mode, type, id, buflen, num_req, max_req;
|
u_int handle_len, mode, type, id, buflen, num_req, max_req;
|
||||||
off_t progress_counter;
|
off_t progress_counter;
|
||||||
|
struct stat st;
|
||||||
struct request {
|
struct request {
|
||||||
u_int id;
|
u_int id;
|
||||||
u_int len;
|
u_int len;
|
||||||
@ -1050,21 +1051,36 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
|
|||||||
return(-1);
|
return(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
|
local_fd = open(local_path, O_WRONLY | O_CREAT | (resume ? : O_TRUNC),
|
||||||
mode | S_IWUSR);
|
mode | S_IWUSR);
|
||||||
if (local_fd == -1) {
|
if (local_fd == -1) {
|
||||||
error("Couldn't open local file \"%s\" for writing: %s",
|
error("Couldn't open local file \"%s\" for writing: %s",
|
||||||
local_path, strerror(errno));
|
local_path, strerror(errno));
|
||||||
do_close(conn, handle, handle_len);
|
goto fail;
|
||||||
buffer_free(&msg);
|
}
|
||||||
free(handle);
|
offset = highwater = 0;
|
||||||
return(-1);
|
if (resume) {
|
||||||
|
if (fstat(local_fd, &st) == -1) {
|
||||||
|
error("Unable to stat local file \"%s\": %s",
|
||||||
|
local_path, strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if ((size_t)st.st_size > size) {
|
||||||
|
error("Unable to resume download of \"%s\": "
|
||||||
|
"local file is larger than remote", local_path);
|
||||||
|
fail:
|
||||||
|
do_close(conn, handle, handle_len);
|
||||||
|
buffer_free(&msg);
|
||||||
|
free(handle);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
offset = highwater = st.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read from remote and write to local */
|
/* Read from remote and write to local */
|
||||||
write_error = read_error = write_errno = num_req = offset = 0;
|
write_error = read_error = write_errno = num_req = 0;
|
||||||
max_req = 1;
|
max_req = 1;
|
||||||
progress_counter = 0;
|
progress_counter = offset;
|
||||||
|
|
||||||
if (showprogress && size != 0)
|
if (showprogress && size != 0)
|
||||||
start_progress_meter(remote_path, size, &progress_counter);
|
start_progress_meter(remote_path, size, &progress_counter);
|
||||||
@ -1139,6 +1155,10 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
|
|||||||
write_error = 1;
|
write_error = 1;
|
||||||
max_req = 0;
|
max_req = 0;
|
||||||
}
|
}
|
||||||
|
else if (!reordered && req->offset <= highwater)
|
||||||
|
highwater = req->offset + len;
|
||||||
|
else if (!reordered && req->offset > highwater)
|
||||||
|
reordered = 1;
|
||||||
progress_counter += len;
|
progress_counter += len;
|
||||||
free(data);
|
free(data);
|
||||||
|
|
||||||
@ -1187,7 +1207,15 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
|
|||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
if (TAILQ_FIRST(&requests) != NULL)
|
if (TAILQ_FIRST(&requests) != NULL)
|
||||||
fatal("Transfer complete, but requests still in queue");
|
fatal("Transfer complete, but requests still in queue");
|
||||||
|
/* Truncate at highest contiguous point to avoid holes on interrupt */
|
||||||
|
if (read_error || write_error || interrupted) {
|
||||||
|
if (reordered && resume) {
|
||||||
|
error("Unable to resume download of \"%s\": "
|
||||||
|
"server reordered requests", local_path);
|
||||||
|
}
|
||||||
|
debug("truncating at %llu", (unsigned long long)highwater);
|
||||||
|
ftruncate(local_fd, highwater);
|
||||||
|
}
|
||||||
if (read_error) {
|
if (read_error) {
|
||||||
error("Couldn't read from remote file \"%s\" : %s",
|
error("Couldn't read from remote file \"%s\" : %s",
|
||||||
remote_path, fx2txt(status));
|
remote_path, fx2txt(status));
|
||||||
@ -1199,7 +1227,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
|
|||||||
do_close(conn, handle, handle_len);
|
do_close(conn, handle, handle_len);
|
||||||
} else {
|
} else {
|
||||||
status = do_close(conn, handle, handle_len);
|
status = do_close(conn, handle, handle_len);
|
||||||
|
if (interrupted)
|
||||||
|
status = -1;
|
||||||
/* Override umask and utimes if asked */
|
/* Override umask and utimes if asked */
|
||||||
#ifdef HAVE_FCHMOD
|
#ifdef HAVE_FCHMOD
|
||||||
if (pflag && fchmod(local_fd, mode) == -1)
|
if (pflag && fchmod(local_fd, mode) == -1)
|
||||||
@ -1227,7 +1256,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
|
download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
|
||||||
Attrib *dirattrib, int pflag, int printflag, int depth)
|
Attrib *dirattrib, int pflag, int printflag, int depth, int resume)
|
||||||
{
|
{
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
SFTP_DIRENT **dir_entries;
|
SFTP_DIRENT **dir_entries;
|
||||||
@ -1280,11 +1309,11 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
|
|||||||
continue;
|
continue;
|
||||||
if (download_dir_internal(conn, new_src, new_dst,
|
if (download_dir_internal(conn, new_src, new_dst,
|
||||||
&(dir_entries[i]->a), pflag, printflag,
|
&(dir_entries[i]->a), pflag, printflag,
|
||||||
depth + 1) == -1)
|
depth + 1, resume) == -1)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
} else if (S_ISREG(dir_entries[i]->a.perm) ) {
|
} else if (S_ISREG(dir_entries[i]->a.perm) ) {
|
||||||
if (do_download(conn, new_src, new_dst,
|
if (do_download(conn, new_src, new_dst,
|
||||||
&(dir_entries[i]->a), pflag) == -1) {
|
&(dir_entries[i]->a), pflag, resume) == -1) {
|
||||||
error("Download of file %s to %s failed",
|
error("Download of file %s to %s failed",
|
||||||
new_src, new_dst);
|
new_src, new_dst);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
@ -1317,7 +1346,7 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
|
|||||||
|
|
||||||
int
|
int
|
||||||
download_dir(struct sftp_conn *conn, char *src, char *dst,
|
download_dir(struct sftp_conn *conn, char *src, char *dst,
|
||||||
Attrib *dirattrib, int pflag, int printflag)
|
Attrib *dirattrib, int pflag, int printflag, int resume)
|
||||||
{
|
{
|
||||||
char *src_canon;
|
char *src_canon;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1328,7 +1357,7 @@ download_dir(struct sftp_conn *conn, char *src, char *dst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = download_dir_internal(conn, src_canon, dst,
|
ret = download_dir_internal(conn, src_canon, dst,
|
||||||
dirattrib, pflag, printflag, 0);
|
dirattrib, pflag, printflag, 0, resume);
|
||||||
free(src_canon);
|
free(src_canon);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1553,7 +1582,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
|
|||||||
a.perm &= 01777;
|
a.perm &= 01777;
|
||||||
if (!pflag)
|
if (!pflag)
|
||||||
a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
|
a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
|
||||||
|
|
||||||
status = do_mkdir(conn, dst, &a, 0);
|
status = do_mkdir(conn, dst, &a, 0);
|
||||||
/*
|
/*
|
||||||
* we lack a portable status for errno EEXIST,
|
* we lack a portable status for errno EEXIST,
|
||||||
@ -1563,7 +1592,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
|
|||||||
if (status != SSH2_FX_OK) {
|
if (status != SSH2_FX_OK) {
|
||||||
if (status != SSH2_FX_FAILURE)
|
if (status != SSH2_FX_FAILURE)
|
||||||
return -1;
|
return -1;
|
||||||
if (do_stat(conn, dst, 0) == NULL)
|
if (do_stat(conn, dst, 0) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1571,7 +1600,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
|
|||||||
error("Failed to open dir \"%s\": %s", src, strerror(errno));
|
error("Failed to open dir \"%s\": %s", src, strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (((dp = readdir(dirp)) != NULL) && !interrupted) {
|
while (((dp = readdir(dirp)) != NULL) && !interrupted) {
|
||||||
if (dp->d_ino == 0)
|
if (dp->d_ino == 0)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: sftp-client.h,v 1.20 2010/12/04 00:18:01 djm Exp $ */
|
/* $OpenBSD: sftp-client.h,v 1.21 2013/07/25 00:56:51 djm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||||
@ -106,13 +106,13 @@ int do_symlink(struct sftp_conn *, char *, char *);
|
|||||||
* Download 'remote_path' to 'local_path'. Preserve permissions and times
|
* Download 'remote_path' to 'local_path'. Preserve permissions and times
|
||||||
* if 'pflag' is set
|
* if 'pflag' is set
|
||||||
*/
|
*/
|
||||||
int do_download(struct sftp_conn *, char *, char *, Attrib *, int);
|
int do_download(struct sftp_conn *, char *, char *, Attrib *, int, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recursively download 'remote_directory' to 'local_directory'. Preserve
|
* Recursively download 'remote_directory' to 'local_directory'. Preserve
|
||||||
* times if 'pflag' is set
|
* times if 'pflag' is set
|
||||||
*/
|
*/
|
||||||
int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int);
|
int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Upload 'local_path' to 'remote_path'. Preserve permissions and times
|
* Upload 'local_path' to 'remote_path'. Preserve permissions and times
|
||||||
|
28
sftp.1
28
sftp.1
@ -1,4 +1,4 @@
|
|||||||
.\" $OpenBSD: sftp.1,v 1.91 2011/09/05 05:56:13 djm Exp $
|
.\" $OpenBSD: sftp.1,v 1.92 2013/07/25 00:56:51 djm Exp $
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2001 Damien Miller. All rights reserved.
|
.\" 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
|
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
.\"
|
.\"
|
||||||
.Dd $Mdocdate: September 5 2011 $
|
.Dd $Mdocdate: July 25 2013 $
|
||||||
.Dt SFTP 1
|
.Dt SFTP 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -129,7 +129,7 @@ may be used to indicate standard input.
|
|||||||
.Nm
|
.Nm
|
||||||
will abort if any of the following
|
will abort if any of the following
|
||||||
commands fail:
|
commands fail:
|
||||||
.Ic get , put , rename , ln ,
|
.Ic get , put , reget , rename , ln ,
|
||||||
.Ic rm , mkdir , chdir , ls ,
|
.Ic rm , mkdir , chdir , ls ,
|
||||||
.Ic lchdir , chmod , chown ,
|
.Ic lchdir , chmod , chown ,
|
||||||
.Ic chgrp , lpwd , df , symlink ,
|
.Ic chgrp , lpwd , df , symlink ,
|
||||||
@ -343,7 +343,7 @@ extension.
|
|||||||
Quit
|
Quit
|
||||||
.Nm sftp .
|
.Nm sftp .
|
||||||
.It Xo Ic get
|
.It Xo Ic get
|
||||||
.Op Fl Ppr
|
.Op Fl aPpr
|
||||||
.Ar remote-path
|
.Ar remote-path
|
||||||
.Op Ar local-path
|
.Op Ar local-path
|
||||||
.Xc
|
.Xc
|
||||||
@ -363,6 +363,14 @@ is specified, then
|
|||||||
.Ar local-path
|
.Ar local-path
|
||||||
must specify a directory.
|
must specify a directory.
|
||||||
.Pp
|
.Pp
|
||||||
|
If the
|
||||||
|
.Fl a
|
||||||
|
flag is specified, then attempt to resume partial transfers of existing files.
|
||||||
|
Note that resumption assumes that any partial copy of the local file matches
|
||||||
|
the remote copy.
|
||||||
|
If the remote file differs from the partial local copy then the resultant file
|
||||||
|
is likely to be corrupt.
|
||||||
|
.Pp
|
||||||
If either the
|
If either the
|
||||||
.Fl P
|
.Fl P
|
||||||
or
|
or
|
||||||
@ -503,6 +511,18 @@ Display remote working directory.
|
|||||||
.It Ic quit
|
.It Ic quit
|
||||||
Quit
|
Quit
|
||||||
.Nm sftp .
|
.Nm sftp .
|
||||||
|
.It Xo Ic reget
|
||||||
|
.Op Fl Ppr
|
||||||
|
.Ar remote-path
|
||||||
|
.Op Ar local-path
|
||||||
|
.Xc
|
||||||
|
Resume download of
|
||||||
|
.Ar remote-path .
|
||||||
|
Equivalent to
|
||||||
|
.Ic get
|
||||||
|
with the
|
||||||
|
.Fl a
|
||||||
|
flag set.
|
||||||
.It Ic rename Ar oldpath Ar newpath
|
.It Ic rename Ar oldpath Ar newpath
|
||||||
Rename remote file from
|
Rename remote file from
|
||||||
.Ar oldpath
|
.Ar oldpath
|
||||||
|
76
sftp.c
76
sftp.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: sftp.c,v 1.147 2013/07/12 00:20:00 djm Exp $ */
|
/* $OpenBSD: sftp.c,v 1.148 2013/07/25 00:56:52 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||||
*
|
*
|
||||||
@ -88,6 +88,9 @@ int showprogress = 1;
|
|||||||
/* When this option is set, we always recursively download/upload directories */
|
/* When this option is set, we always recursively download/upload directories */
|
||||||
int global_rflag = 0;
|
int global_rflag = 0;
|
||||||
|
|
||||||
|
/* When this option is set, we resume download if possible */
|
||||||
|
int global_aflag = 0;
|
||||||
|
|
||||||
/* When this option is set, the file transfers will always preserve times */
|
/* When this option is set, the file transfers will always preserve times */
|
||||||
int global_pflag = 0;
|
int global_pflag = 0;
|
||||||
|
|
||||||
@ -151,6 +154,7 @@ extern char *__progname;
|
|||||||
#define I_SYMLINK 21
|
#define I_SYMLINK 21
|
||||||
#define I_VERSION 22
|
#define I_VERSION 22
|
||||||
#define I_PROGRESS 23
|
#define I_PROGRESS 23
|
||||||
|
#define I_REGET 26
|
||||||
|
|
||||||
struct CMD {
|
struct CMD {
|
||||||
const char *c;
|
const char *c;
|
||||||
@ -190,6 +194,7 @@ static const struct CMD cmds[] = {
|
|||||||
{ "put", I_PUT, LOCAL },
|
{ "put", I_PUT, LOCAL },
|
||||||
{ "pwd", I_PWD, REMOTE },
|
{ "pwd", I_PWD, REMOTE },
|
||||||
{ "quit", I_QUIT, NOARGS },
|
{ "quit", I_QUIT, NOARGS },
|
||||||
|
{ "reget", I_REGET, REMOTE },
|
||||||
{ "rename", I_RENAME, REMOTE },
|
{ "rename", I_RENAME, REMOTE },
|
||||||
{ "rm", I_RM, REMOTE },
|
{ "rm", I_RM, REMOTE },
|
||||||
{ "rmdir", I_RMDIR, REMOTE },
|
{ "rmdir", I_RMDIR, REMOTE },
|
||||||
@ -239,6 +244,7 @@ help(void)
|
|||||||
" filesystem containing 'path'\n"
|
" filesystem containing 'path'\n"
|
||||||
"exit Quit sftp\n"
|
"exit Quit sftp\n"
|
||||||
"get [-Ppr] remote [local] Download file\n"
|
"get [-Ppr] remote [local] Download file\n"
|
||||||
|
"reget remote [local] Resume download file\n"
|
||||||
"help Display this help text\n"
|
"help Display this help text\n"
|
||||||
"lcd path Change local directory to 'path'\n"
|
"lcd path Change local directory to 'path'\n"
|
||||||
"lls [ls-options [path]] Display local directory listing\n"
|
"lls [ls-options [path]] Display local directory listing\n"
|
||||||
@ -350,8 +356,8 @@ make_absolute(char *p, char *pwd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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 *rflag)
|
int *aflag, int *pflag, int *rflag)
|
||||||
{
|
{
|
||||||
extern int opterr, optind, optopt, optreset;
|
extern int opterr, optind, optopt, optreset;
|
||||||
int ch;
|
int ch;
|
||||||
@ -359,9 +365,12 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
|
|||||||
optind = optreset = 1;
|
optind = optreset = 1;
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
|
|
||||||
*rflag = *pflag = 0;
|
*aflag = *rflag = *pflag = 0;
|
||||||
while ((ch = getopt(argc, argv, "PpRr")) != -1) {
|
while ((ch = getopt(argc, argv, "aPpRr")) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
|
case 'a':
|
||||||
|
*aflag = 1;
|
||||||
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
case 'P':
|
case 'P':
|
||||||
*pflag = 1;
|
*pflag = 1;
|
||||||
@ -519,7 +528,7 @@ pathname_is_dir(char *pathname)
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
|
process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
|
||||||
int pflag, int rflag)
|
int pflag, int rflag, int resume)
|
||||||
{
|
{
|
||||||
char *abs_src = NULL;
|
char *abs_src = NULL;
|
||||||
char *abs_dst = NULL;
|
char *abs_dst = NULL;
|
||||||
@ -571,15 +580,18 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
|
|||||||
}
|
}
|
||||||
free(tmp);
|
free(tmp);
|
||||||
|
|
||||||
if (!quiet)
|
resume |= global_aflag;
|
||||||
|
if (!quiet && resume)
|
||||||
|
printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
|
||||||
|
else if (!quiet && !resume)
|
||||||
printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
|
printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
|
||||||
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
|
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
|
||||||
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
|
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
|
||||||
pflag || global_pflag, 1) == -1)
|
pflag || global_pflag, 1, resume) == -1)
|
||||||
err = -1;
|
err = -1;
|
||||||
} else {
|
} else {
|
||||||
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
|
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
|
||||||
pflag || global_pflag) == -1)
|
pflag || global_pflag, resume) == -1)
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
free(abs_dst);
|
free(abs_dst);
|
||||||
@ -1118,8 +1130,9 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
|
parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag,
|
||||||
int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2)
|
int *pflag, int *rflag, int *sflag, unsigned long *n_arg,
|
||||||
|
char **path1, char **path2)
|
||||||
{
|
{
|
||||||
const char *cmd, *cp = *cpp;
|
const char *cmd, *cp = *cpp;
|
||||||
char *cp2, **argv;
|
char *cp2, **argv;
|
||||||
@ -1163,14 +1176,15 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get arguments and parse flags */
|
/* Get arguments and parse flags */
|
||||||
*lflag = *pflag = *rflag = *hflag = *n_arg = 0;
|
*aflag = *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
|
||||||
*path1 = *path2 = NULL;
|
*path1 = *path2 = NULL;
|
||||||
optidx = 1;
|
optidx = 1;
|
||||||
switch (cmdnum) {
|
switch (cmdnum) {
|
||||||
case I_GET:
|
case I_GET:
|
||||||
|
case I_REGET:
|
||||||
case I_PUT:
|
case I_PUT:
|
||||||
if ((optidx = parse_getput_flags(cmd, argv, argc,
|
if ((optidx = parse_getput_flags(cmd, argv, argc,
|
||||||
pflag, rflag)) == -1)
|
aflag, pflag, rflag)) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
/* Get first pathname (mandatory) */
|
/* Get first pathname (mandatory) */
|
||||||
if (argc - optidx < 1) {
|
if (argc - optidx < 1) {
|
||||||
@ -1185,6 +1199,11 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
|
|||||||
/* Destination is not globbed */
|
/* Destination is not globbed */
|
||||||
undo_glob_escape(*path2);
|
undo_glob_escape(*path2);
|
||||||
}
|
}
|
||||||
|
if (*aflag && cmdnum == I_PUT) {
|
||||||
|
/* XXX implement resume for uploads */
|
||||||
|
error("Resume is not supported for uploads");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case I_LINK:
|
case I_LINK:
|
||||||
if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
|
if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
|
||||||
@ -1293,7 +1312,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
|
|||||||
int err_abort)
|
int err_abort)
|
||||||
{
|
{
|
||||||
char *path1, *path2, *tmp;
|
char *path1, *path2, *tmp;
|
||||||
int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
|
int aflag = 0, hflag = 0, iflag = 0, lflag = 0, pflag = 0;
|
||||||
|
int rflag = 0, sflag = 0;
|
||||||
int cmdnum, i;
|
int cmdnum, i;
|
||||||
unsigned long n_arg = 0;
|
unsigned long n_arg = 0;
|
||||||
Attrib a, *aa;
|
Attrib a, *aa;
|
||||||
@ -1302,9 +1322,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
|
|||||||
glob_t g;
|
glob_t g;
|
||||||
|
|
||||||
path1 = path2 = NULL;
|
path1 = path2 = NULL;
|
||||||
cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
|
cmdnum = parse_args(&cmd, &aflag, &hflag, &iflag, &lflag, &pflag,
|
||||||
&sflag, &n_arg, &path1, &path2);
|
&rflag, &sflag, &n_arg, &path1, &path2);
|
||||||
|
|
||||||
if (iflag != 0)
|
if (iflag != 0)
|
||||||
err_abort = 0;
|
err_abort = 0;
|
||||||
|
|
||||||
@ -1319,8 +1338,12 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
|
|||||||
/* Unrecognized command */
|
/* Unrecognized command */
|
||||||
err = -1;
|
err = -1;
|
||||||
break;
|
break;
|
||||||
|
case I_REGET:
|
||||||
|
aflag = 1;
|
||||||
|
/* FALLTHROUGH */
|
||||||
case I_GET:
|
case I_GET:
|
||||||
err = process_get(conn, path1, path2, *pwd, pflag, rflag);
|
err = process_get(conn, path1, path2, *pwd, pflag,
|
||||||
|
rflag, aflag);
|
||||||
break;
|
break;
|
||||||
case I_PUT:
|
case I_PUT:
|
||||||
err = process_put(conn, path1, path2, *pwd, pflag, rflag);
|
err = process_put(conn, path1, path2, *pwd, pflag, rflag);
|
||||||
@ -1949,12 +1972,10 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* XXX this is wrong wrt quoting */
|
/* XXX this is wrong wrt quoting */
|
||||||
if (file2 == NULL)
|
snprintf(cmd, sizeof cmd, "get%s %s%s%s",
|
||||||
snprintf(cmd, sizeof cmd, "get %s", dir);
|
global_aflag ? " -a" : "", dir,
|
||||||
else
|
file2 == NULL ? "" : " ",
|
||||||
snprintf(cmd, sizeof cmd, "get %s %s", dir,
|
file2 == NULL ? "" : file2);
|
||||||
file2);
|
|
||||||
|
|
||||||
err = parse_dispatch_command(conn, cmd,
|
err = parse_dispatch_command(conn, cmd,
|
||||||
&remote_path, 1);
|
&remote_path, 1);
|
||||||
free(dir);
|
free(dir);
|
||||||
@ -2143,7 +2164,7 @@ main(int argc, char **argv)
|
|||||||
infile = stdin;
|
infile = stdin;
|
||||||
|
|
||||||
while ((ch = getopt(argc, argv,
|
while ((ch = getopt(argc, argv,
|
||||||
"1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
|
"1246ahpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
/* Passed through to ssh(1) */
|
/* Passed through to ssh(1) */
|
||||||
case '4':
|
case '4':
|
||||||
@ -2183,6 +2204,9 @@ main(int argc, char **argv)
|
|||||||
case '2':
|
case '2':
|
||||||
sshver = 2;
|
sshver = 2;
|
||||||
break;
|
break;
|
||||||
|
case 'a':
|
||||||
|
global_aflag = 1;
|
||||||
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
copy_buffer_len = strtol(optarg, &cp, 10);
|
copy_buffer_len = strtol(optarg, &cp, 10);
|
||||||
if (copy_buffer_len == 0 || *cp != '\0')
|
if (copy_buffer_len == 0 || *cp != '\0')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user