Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

at least partial support to scp >2GB files on windows #31

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CMakeLists.txt
Expand Up @@ -119,6 +119,7 @@ set(MAN_PAGES
libssh2_publickey_remove_ex.3
libssh2_publickey_shutdown.3
libssh2_scp_recv.3
libssh2_scp_recv2.3
libssh2_scp_send.3
libssh2_scp_send64.3
libssh2_scp_send_ex.3
Expand Down
1 change: 1 addition & 0 deletions docs/Makefile.am
Expand Up @@ -89,6 +89,7 @@ dist_man_MANS = \
libssh2_publickey_remove_ex.3 \
libssh2_publickey_shutdown.3 \
libssh2_scp_recv.3 \
libssh2_scp_recv2.3 \
libssh2_scp_send.3 \
libssh2_scp_send64.3 \
libssh2_scp_send_ex.3 \
Expand Down
3 changes: 3 additions & 0 deletions docs/libssh2_scp_recv.3
Expand Up @@ -8,6 +8,9 @@ LIBSSH2_CHANNEL *
libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb);

.SH DESCRIPTION
This function is \fBDEPRECATED\fP. Use \fIlibssh2_scp_recv2(3)\fP
instead!

\fIsession\fP - Session instance as returned by
.BR libssh2_session_init_ex(3)

Expand Down
32 changes: 32 additions & 0 deletions docs/libssh2_scp_recv2.3
@@ -0,0 +1,32 @@
.TH libssh2_scp_recv2 3 "29 Jun 2015" "libssh2 1.6.1" "libssh2 manual"
.SH NAME
libssh2_scp_recv2 - request a remote file via SCP
.SH SYNOPSIS
#include <libssh2.h>

LIBSSH2_CHANNEL *
libssh2_scp_recv2(LIBSSH2_SESSION *session, const char *path, struct_stat *sb);

.SH DESCRIPTION
\fIsession\fP - Session instance as returned by
.BR libssh2_session_init_ex(3)

\fIpath\fP - Full path and filename of file to transfer. That is the remote
file name.

\fIsb\fP - Populated with remote file's size, mode, mtime, and atime

Request a file from the remote host via SCP.
.SH RETURN VALUE
Pointer to a newly allocated LIBSSH2_CHANNEL instance, or NULL on errors.
.SH ERRORS
\fILIBSSH2_ERROR_ALLOC\fP - An internal memory allocation call failed.

\fILIBSSH2_ERROR_SCP_PROTOCOL\fP -

\fILIBSSH2_ERROR_EAGAIN\fP - Marked for non-blocking I/O but the call would
block.
.SH SEE ALSO
.BR libssh2_session_init_ex(3)
.BR libssh2_channel_open_ex(3)

8 changes: 4 additions & 4 deletions example/scp.c
Expand Up @@ -41,9 +41,9 @@ int main(int argc, char *argv[])
const char *username="username";
const char *password="password";
const char *scppath="/tmp/TEST";
struct stat fileinfo;
libssh2_struct_stat fileinfo;
int rc;
off_t got=0;
libssh2_struct_stat_size got = 0;

#ifdef WIN32
WSADATA wsadata;
Expand Down Expand Up @@ -137,7 +137,7 @@ int main(int argc, char *argv[])
}

/* Request a file via SCP */
channel = libssh2_scp_recv(session, scppath, &fileinfo);
channel = libssh2_scp_recv2(session, scppath, &fileinfo);

if (!channel) {
fprintf(stderr, "Unable to open a session: %d\n",
Expand All @@ -151,7 +151,7 @@ int main(int argc, char *argv[])
int amount=sizeof(mem);

if((fileinfo.st_size -got) < amount) {
amount = fileinfo.st_size -got;
amount = (int)(fileinfo.st_size -got);
}

rc = libssh2_channel_read(channel, mem, amount);
Expand Down
18 changes: 9 additions & 9 deletions example/scp_nonblock.c
Expand Up @@ -88,16 +88,16 @@ int main(int argc, char *argv[])
const char *username="username";
const char *password="password";
const char *scppath="/tmp/TEST";
struct stat fileinfo;
libssh2_struct_stat fileinfo;
#ifdef HAVE_GETTIMEOFDAY
struct timeval start;
struct timeval end;
long time_ms;
#endif
int rc;
int total = 0;
int spin = 0;
off_t got=0;
libssh2_struct_stat_size got = 0;
libssh2_struct_stat_size total = 0;

#ifdef WIN32
WSADATA wsadata;
Expand Down Expand Up @@ -207,9 +207,9 @@ int main(int argc, char *argv[])
#endif

/* Request a file via SCP */
fprintf(stderr, "libssh2_scp_recv()!\n");
fprintf(stderr, "libssh2_scp_recv2()!\n");
do {
channel = libssh2_scp_recv(session, scppath, &fileinfo);
channel = libssh2_scp_recv2(session, scppath, &fileinfo);

if (!channel) {
if(libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) {
Expand All @@ -235,7 +235,7 @@ int main(int argc, char *argv[])
int amount=sizeof(mem);

if ((fileinfo.st_size -got) < amount) {
amount = fileinfo.st_size - got;
amount = (int)(fileinfo.st_size - got);
}

/* loop until we block */
Expand All @@ -262,10 +262,10 @@ int main(int argc, char *argv[])
gettimeofday(&end, NULL);

time_ms = tvdiff(end, start);
fprintf(stderr, "Got %d bytes in %ld ms = %.1f bytes/sec spin: %d\n", total,
time_ms, total/(time_ms/1000.0), spin );
fprintf(stderr, "Got " LIBSSH2_STRUCT_STAT_SIZE_FORMAT " bytes in %ld ms = %.1f bytes/sec spin: %d\n", total,
time_ms, total/(time_ms/1000.0), spin);
#else
fprintf(stderr, "Got %d bytes spin: %d\n", total, spin);
fprintf(stderr, "Got " LIBSSH2_STRUCT_STAT_SIZE_FORMAT " bytes spin: %d\n", total, spin);
#endif

libssh2_channel_free(channel);
Expand Down
2 changes: 1 addition & 1 deletion example/ssh2.c
Expand Up @@ -243,7 +243,7 @@ int main(int argc, char *argv[])

/* Other channel types are supported via:
* libssh2_scp_send()
* libssh2_scp_recv()
* libssh2_scp_recv2()
* libssh2_channel_direct_tcpip()
*/

Expand Down
2 changes: 1 addition & 1 deletion example/ssh2_agent.c
Expand Up @@ -217,7 +217,7 @@ int main(int argc, char *argv[])

/* Other channel types are supported via:
* libssh2_scp_send()
* libssh2_scp_recv()
* libssh2_scp_recv2()
* libssh2_channel_direct_tcpip()
*/

Expand Down
66 changes: 66 additions & 0 deletions include/libssh2.h
Expand Up @@ -145,6 +145,67 @@ typedef int libssh2_socket_t;
#define LIBSSH2_INVALID_SOCKET -1
#endif /* WIN32 */

/*
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is brutally lifted from https://github.com/bagder/curl/blob/master/lib/config-win32.h#L631. I'd love for it to live in a win32-specific place, but since this is public and doesn't depend on anything at configure-time this is what I went with.

* Determine whether there is small or large file support on windows.
*/

#if defined(_MSC_VER) && !defined(_WIN32_WCE)
# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
# define USE_WIN32_LARGE_FILES
# else
# define USE_WIN32_SMALL_FILES
# endif
#endif

#if defined(__MINGW32__) && !defined(USE_WIN32_LARGE_FILES)
# define USE_WIN32_LARGE_FILES
#endif

#if defined(__WATCOMC__) && !defined(USE_WIN32_LARGE_FILES)
# define USE_WIN32_LARGE_FILES
#endif

#if defined(__POCC__)
# undef USE_WIN32_LARGE_FILES
#endif

#if defined(_WIN32) && !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES)
# define USE_WIN32_SMALL_FILES
#endif

/*
* Large file (>2Gb) support using WIN32 functions.
*/

#ifdef USE_WIN32_LARGE_FILES
# include <io.h>
# include <sys/types.h>
# include <sys/stat.h>
# define LIBSSH2_STRUCT_STAT_SIZE_FORMAT "%I64d"
typedef struct _stati64 libssh2_struct_stat;
typedef __int64 libssh2_struct_stat_size;
#endif

/*
* Small file (<2Gb) support using WIN32 functions.
*/

#ifdef USE_WIN32_SMALL_FILES
# include <sys/types.h>
# include <sys/stat.h>
# ifndef _WIN32_WCE
# define LIBSSH2_STRUCT_STAT_SIZE_FORMAT "%d"
typedef struct _stat libssh2_struct_stat;
typedef off_t libssh2_struct_stat_size;
# endif
#endif

#ifndef LIBSSH2_STRUCT_STAT_SIZE_FORMAT
# define LIBSSH2_STRUCT_STAT_SIZE_FORMAT "%zd"
typedef struct stat libssh2_struct_stat;
typedef off_t libssh2_struct_stat_size;
#endif

/* Part of every banner, user specified or not */
#define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION

Expand Down Expand Up @@ -805,9 +866,14 @@ LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel);
LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel);
LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel);

/* libssh2_scp_recv is DEPRECATED, do not use! */
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session,
const char *path,
struct stat *sb);
/* Use libssh2_scp_recv2 for large (> 2GB) file support on windows */
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv2(LIBSSH2_SESSION *session,
const char *path,
libssh2_struct_stat *sb);
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session,
const char *path, int mode,
size_t size, long mtime,
Expand Down
1 change: 1 addition & 0 deletions nw/GNUmakefile
Expand Up @@ -545,6 +545,7 @@ endif
@echo $(DL) libssh2_knownhost_readfile,$(DL) >> $@
@echo $(DL) libssh2_knownhost_writefile,$(DL) >> $@
@echo $(DL) libssh2_scp_recv,$(DL) >> $@
@echo $(DL) libssh2_scp_recv2,$(DL) >> $@
@echo $(DL) libssh2_scp_send64,$(DL) >> $@
@echo $(DL) libssh2_scp_send_ex,$(DL) >> $@
@echo $(DL) libssh2_session_abstract,$(DL) >> $@
Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -38,6 +38,7 @@ include(CheckSymbolExists)
include(CheckFunctionExistsMayNeedLibrary)
include(CheckIncludeFiles)
include(CheckTypeSize)
include(CheckSymbolExists)
include(CheckNonblockingSocketSupport)
include(SocketLibraries)

Expand Down Expand Up @@ -281,6 +282,10 @@ if(HAVE_STDLIB_H)
else()
check_function_exists(strtoll HAVE_STRTOLL)
endif()
if (NOT HAVE_STRTOLL)
# Try _strtoi64 if strtoll isn't available
check_symbol_exists(_strtoi64 stdlib.h HAVE_STRTOI64)
endif()
check_symbol_exists(snprintf stdio.h HAVE_SNPRINTF)

if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
Expand Down
1 change: 1 addition & 0 deletions src/libssh2_config_cmake.h.in
Expand Up @@ -64,6 +64,7 @@
#cmakedefine HAVE_SELECT
#cmakedefine HAVE_SOCKET
#cmakedefine HAVE_STRTOLL
#cmakedefine HAVE_STRTOI64
#cmakedefine HAVE_SNPRINTF

/* OpenSSL functions */
Expand Down
5 changes: 4 additions & 1 deletion src/libssh2_priv.h
Expand Up @@ -779,7 +779,7 @@ struct _LIBSSH2_SESSION
int sftpInit_sent; /* number of bytes from the buffer that have been
sent */

/* State variables used in libssh2_scp_recv() */
/* State variables used in libssh2_scp_recv() / libssh_scp_recv2() */
libssh2_nonblocking_states scpRecv_state;
unsigned char *scpRecv_command;
size_t scpRecv_command_len;
Expand All @@ -790,6 +790,9 @@ struct _LIBSSH2_SESSION
/* we have the type and we can parse such numbers */
long long scpRecv_size;
#define scpsize_strtol strtoll
#elif defined(HAVE_STRTOI64)
__int64 scpRecv_size;
#define scpsize_strtol _strtoi64
#else
long scpRecv_size;
#define scpsize_strtol strtol
Expand Down
42 changes: 39 additions & 3 deletions src/scp.c
Expand Up @@ -268,7 +268,7 @@ shell_quotearg(const char *path, unsigned char *buf,
*
*/
static LIBSSH2_CHANNEL *
scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb)
scp_recv(LIBSSH2_SESSION * session, const char *path, libssh2_struct_stat * sb)
{
int cmd_len;
int rc;
Expand Down Expand Up @@ -724,7 +724,7 @@ scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb)
}

if (sb) {
memset(sb, 0, sizeof(struct stat));
memset(sb, 0, sizeof(libssh2_struct_stat));

sb->st_mtime = session->scpRecv_mtime;
sb->st_atime = session->scpRecv_atime;
Expand Down Expand Up @@ -759,11 +759,47 @@ scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb)
/*
* libssh2_scp_recv
*
* Open a channel and request a remote file via SCP
* DEPRECATED
*
* Open a channel and request a remote file via SCP. This receives files larger
* than 2 GB, but is unable to report the proper size on platforms where the
* st_size member of struct stat is limited to 2 GB (e.g. windows).
*
*/
LIBSSH2_API LIBSSH2_CHANNEL *
libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat * sb)
{
LIBSSH2_CHANNEL *ptr;

/* scp_recv uses libssh2_struct_stat, so pass one if the caller gave us a struct to populate... */
libssh2_struct_stat sb_intl;
libssh2_struct_stat *sb_ptr;
sb_ptr = sb ? &sb_intl : NULL;

BLOCK_ADJUST_ERRNO(ptr, session, scp_recv(session, path, sb_ptr));

/* ...and populate the caller's with as much info as fits. */
if (sb) {
memset(sb, 0, sizeof(struct stat));

sb->st_mtime = sb_intl.st_mtime;
sb->st_atime = sb_intl.st_atime;
sb->st_size = (off_t)sb_intl.st_size;
sb->st_mode = sb_intl.st_mode;
}

return ptr;
}

/*
* libssh2_scp_recv2
*
* Open a channel and request a remote file via SCP. This supports files > 2GB
* on platforms that support it.
*
*/
LIBSSH2_API LIBSSH2_CHANNEL *
libssh2_scp_recv2(LIBSSH2_SESSION *session, const char *path, libssh2_struct_stat * sb)
{
LIBSSH2_CHANNEL *ptr;
BLOCK_ADJUST_ERRNO(ptr, session, scp_recv(session, path, sb));
Expand Down