diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index bbcac97a..fd6fcf2c 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -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 diff --git a/docs/Makefile.am b/docs/Makefile.am index 6436a75f..fa8188f4 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -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 \ diff --git a/docs/libssh2_scp_recv.3 b/docs/libssh2_scp_recv.3 index 669d7e10..2edcdd48 100644 --- a/docs/libssh2_scp_recv.3 +++ b/docs/libssh2_scp_recv.3 @@ -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) diff --git a/docs/libssh2_scp_recv2.3 b/docs/libssh2_scp_recv2.3 new file mode 100644 index 00000000..6b48fd14 --- /dev/null +++ b/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_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) + diff --git a/example/scp.c b/example/scp.c index 5e7eb993..51dd3839 100644 --- a/example/scp.c +++ b/example/scp.c @@ -41,7 +41,7 @@ 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; @@ -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", @@ -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); diff --git a/example/scp_nonblock.c b/example/scp_nonblock.c index 9b19d77a..45f66b83 100644 --- a/example/scp_nonblock.c +++ b/example/scp_nonblock.c @@ -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; @@ -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) { @@ -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 */ @@ -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); diff --git a/example/ssh2.c b/example/ssh2.c index d9a88efb..f9ee68ae 100644 --- a/example/ssh2.c +++ b/example/ssh2.c @@ -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() */ diff --git a/example/ssh2_agent.c b/example/ssh2_agent.c index 84f3122a..33a2998a 100644 --- a/example/ssh2_agent.c +++ b/example/ssh2_agent.c @@ -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() */ diff --git a/include/libssh2.h b/include/libssh2.h index 06469299..6d599372 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -145,6 +145,67 @@ typedef int libssh2_socket_t; #define LIBSSH2_INVALID_SOCKET -1 #endif /* WIN32 */ +/* + * 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 +# include +# include +# 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 +# include +# 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 @@ -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, diff --git a/nw/GNUmakefile b/nw/GNUmakefile index 2a1029a3..0a1d0b39 100644 --- a/nw/GNUmakefile +++ b/nw/GNUmakefile @@ -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) >> $@ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14ab5ee5..1a76399e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,6 +38,7 @@ include(CheckSymbolExists) include(CheckFunctionExistsMayNeedLibrary) include(CheckIncludeFiles) include(CheckTypeSize) +include(CheckSymbolExists) include(CheckNonblockingSocketSupport) include(SocketLibraries) @@ -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 diff --git a/src/libssh2_config_cmake.h.in b/src/libssh2_config_cmake.h.in index 631e329d..62723ede 100644 --- a/src/libssh2_config_cmake.h.in +++ b/src/libssh2_config_cmake.h.in @@ -64,6 +64,7 @@ #cmakedefine HAVE_SELECT #cmakedefine HAVE_SOCKET #cmakedefine HAVE_STRTOLL +#cmakedefine HAVE_STRTOI64 #cmakedefine HAVE_SNPRINTF /* OpenSSL functions */ diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index efc917c7..6c23b3de 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -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; @@ -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 diff --git a/src/scp.c b/src/scp.c index c56f46ce..22778dd3 100644 --- a/src/scp.c +++ b/src/scp.c @@ -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; @@ -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; @@ -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));