@@ -5901,6 +5901,24 @@ static int sftpPutToCompletion(WOLFSSH* ssh, char* from, char* to, byte resume)
59015901
59025902 return ret ;
59035903}
5904+
5905+
5906+ /* Drives one wolfSSH_SFTP_Get() to a terminal result. */
5907+ static int sftpGetToCompletion (WOLFSSH * ssh , char * from , char * to , byte resume )
5908+ {
5909+ int ret = WS_FATAL_ERROR ;
5910+ int tries ;
5911+
5912+ for (tries = 0 ; tries < SFTP_MAX_RETRY_TRIES ; tries ++ ) {
5913+ ret = wolfSSH_SFTP_Get (ssh , from , to , resume , NULL );
5914+ if (ret == WS_SUCCESS ||
5915+ !sftp_error_is_notice (wolfSSH_get_error (ssh ))) {
5916+ break ;
5917+ }
5918+ }
5919+
5920+ return ret ;
5921+ }
59045922#endif /* !NO_FILESYSTEM && !WOLFSSH_USER_FILESYSTEM && !WOLFSSH_ZEPHYR */
59055923
59065924
@@ -6031,9 +6049,141 @@ static void test_wolfSSH_SFTP_PutResume(void)
60316049
60326050 wolfSSH_free (ssh );
60336051 wolfSSH_CTX_free (ctx );
6034- #ifdef WOLFSSH_ZEPHYR
6035- k_sleep (Z_TIMEOUT_TICKS (100 ));
6052+ ThreadJoin (serThread );
6053+ FreeTcpReady (& ready );
6054+ #endif /* !NO_FILESYSTEM && !WOLFSSH_USER_FILESYSTEM && !WOLFSSH_ZEPHYR */
6055+ }
6056+
6057+
6058+ /* A resumed get must keep the bytes already at the local destination, and
6059+ * must only resume onto one whose size matches the saved offset. The
6060+ * echoserver shares this process, so the "remote" file is local. */
6061+ static void test_wolfSSH_SFTP_GetResume (void )
6062+ {
6063+ /* staging both files needs the hosted fopen()/getcwd() wrappers */
6064+ #if !defined(NO_FILESYSTEM ) && !defined(WOLFSSH_USER_FILESYSTEM ) && \
6065+ !defined(WOLFSSH_ZEPHYR )
6066+ func_args ser ;
6067+ tcp_ready ready ;
6068+ int argsCount ;
6069+ WS_SOCKET_T clientFd ;
6070+
6071+ const char * args [10 ];
6072+ WOLFSSH_CTX * ctx = NULL ;
6073+ WOLFSSH * ssh = NULL ;
6074+
6075+ THREAD_TYPE serThread ;
6076+
6077+ byte src [SFTP_PUT_RESUME_SZ ];
6078+ byte stale [SFTP_PUT_RESUME_SZ + SFTP_PUT_RESUME_OFST ];
6079+ byte expect [SFTP_PUT_RESUME_SZ ];
6080+ word32 ofst [2 ];
6081+ char srcName [] = "wolfssh_12547_src.tmp" ;
6082+ char dstName [] = "wolfssh_12547_dst.tmp" ;
6083+
6084+ WMEMSET (& ser , 0 , sizeof (func_args ));
6085+
6086+ argsCount = 0 ;
6087+ args [argsCount ++ ] = "." ;
6088+ args [argsCount ++ ] = "-1" ;
6089+ args [argsCount ++ ] = "-p" ;
6090+ args [argsCount ++ ] = "0" ;
6091+ ser .argv = (char * * )args ;
6092+ ser .argc = argsCount ;
6093+ ser .signal = & ready ;
6094+ InitTcpReady (ser .signal );
6095+ ThreadStart (echoserver_test , (void * )& ser , & serThread );
6096+ WaitTcpReady (& ready );
6097+
6098+ sftp_client_connect (& ctx , & ssh , ready .port );
6099+ AssertNotNull (ctx );
6100+ AssertNotNull (ssh );
6101+
6102+ sftpPutFillPattern (src , (word32 )sizeof (src ));
6103+ WMEMSET (stale , 0xFF , sizeof (stale ));
6104+ AssertIntEQ (sftpPutWriteFile (srcName , src , (word32 )sizeof (src )), 0 );
6105+
6106+ /* the saved offset matches the destination, so the download picks up
6107+ * where it left off. Staging a prefix unlike the source, and expecting it
6108+ * back untouched, is what separates a resume from a full re-download. */
6109+ AssertIntEQ (sftpPutWriteFile (dstName , stale , SFTP_PUT_RESUME_OFST ), 0 );
6110+ WMEMCPY (expect , stale , SFTP_PUT_RESUME_OFST );
6111+ WMEMCPY (expect + SFTP_PUT_RESUME_OFST , src + SFTP_PUT_RESUME_OFST ,
6112+ sizeof (expect ) - SFTP_PUT_RESUME_OFST );
6113+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6114+ ofst [1 ] = 0 ;
6115+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6116+ WS_SUCCESS );
6117+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6118+ AssertIntEQ (sftpPutFileMatches (dstName , expect , (word32 )sizeof (expect )),
6119+ 0 );
6120+
6121+ /* the destination is gone, so the whole file is fetched again */
6122+ WREMOVE (NULL , dstName );
6123+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6124+ ofst [1 ] = 0 ;
6125+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6126+ WS_SUCCESS );
6127+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6128+ AssertIntEQ (sftpPutFileMatches (dstName , src , (word32 )sizeof (src )), 0 );
6129+
6130+ /* the destination is shorter than the saved offset, so the whole file is
6131+ * fetched again */
6132+ AssertIntEQ (sftpPutWriteFile (dstName , stale , SFTP_PUT_RESUME_OFST / 2 ), 0 );
6133+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6134+ ofst [1 ] = 0 ;
6135+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6136+ WS_SUCCESS );
6137+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6138+ AssertIntEQ (sftpPutFileMatches (dstName , src , (word32 )sizeof (src )), 0 );
6139+
6140+ /* the destination is longer than the saved offset, so the whole file is
6141+ * fetched again */
6142+ AssertIntEQ (sftpPutWriteFile (dstName , stale , SFTP_PUT_RESUME_OFST * 2 ), 0 );
6143+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6144+ ofst [1 ] = 0 ;
6145+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6146+ WS_SUCCESS );
6147+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6148+ AssertIntEQ (sftpPutFileMatches (dstName , src , (word32 )sizeof (src )), 0 );
6149+
6150+ /* a plain get still truncates a longer destination */
6151+ AssertIntEQ (sftpPutWriteFile (dstName , stale , (word32 )sizeof (stale )), 0 );
6152+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 0 ), WS_SUCCESS );
6153+ AssertIntEQ (sftpPutFileMatches (dstName , src , (word32 )sizeof (src )), 0 );
6154+
6155+ /* the source has nothing left past the saved offset, so the whole file
6156+ * is fetched again. This case shortens the source, so it runs last. */
6157+ AssertIntEQ (sftpPutWriteFile (dstName , stale , SFTP_PUT_RESUME_OFST ), 0 );
6158+ AssertIntEQ (sftpPutWriteFile (srcName , src , SFTP_PUT_RESUME_OFST / 2 ), 0 );
6159+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6160+ ofst [1 ] = 0 ;
6161+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6162+ WS_SUCCESS );
6163+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6164+ AssertIntEQ (sftpPutFileMatches (dstName , src , SFTP_PUT_RESUME_OFST / 2 ), 0 );
6165+
6166+ WREMOVE (NULL , dstName );
6167+ WREMOVE (NULL , srcName );
6168+
6169+ /* take care of re-keying state before shutdown call */
6170+ while (wolfSSH_get_error (ssh ) == WS_REKEYING ) {
6171+ wolfSSH_worker (ssh , NULL );
6172+ }
6173+
6174+ argsCount = AbsorbBenignReset (ssh , wolfSSH_shutdown (ssh ));
6175+ #if DEFAULT_HIGHWATER_MARK < 8000
6176+ if (argsCount == WS_REKEYING ) {
6177+ argsCount = WS_SUCCESS ;
6178+ }
60366179#endif
6180+ AssertIntEQ (argsCount , WS_SUCCESS );
6181+
6182+ clientFd = wolfSSH_get_fd (ssh );
6183+ WCLOSESOCKET (clientFd );
6184+
6185+ wolfSSH_free (ssh );
6186+ wolfSSH_CTX_free (ctx );
60376187 ThreadJoin (serThread );
60386188 FreeTcpReady (& ready );
60396189#endif /* !NO_FILESYSTEM && !WOLFSSH_USER_FILESYSTEM && !WOLFSSH_ZEPHYR */
@@ -6051,6 +6201,7 @@ static void test_wolfSSH_SFTP_SetConfinePath(void) { ; }
60516201static void test_wolfSSH_SFTP_SetDefaultPath (void ) { ; }
60526202static void test_wolfSSH_SFTP_SaveOfst (void ) { ; }
60536203static void test_wolfSSH_SFTP_PutResume (void ) { ; }
6204+ static void test_wolfSSH_SFTP_GetResume (void ) { ; }
60546205#endif /* WOLFSSH_SFTP && !NO_WOLFSSH_CLIENT && !SINGLE_THREADED */
60556206
60566207
@@ -8003,6 +8154,7 @@ int wolfSSH_ApiTest(int argc, char** argv)
80038154 test_wolfSSH_SFTP_SetDefaultPath ();
80048155 test_wolfSSH_SFTP_SaveOfst ();
80058156 test_wolfSSH_SFTP_PutResume ();
8157+ test_wolfSSH_SFTP_GetResume ();
80068158
80078159 /* Either SCP or SFTP */
80088160 test_wolfSSH_RealPath ();
0 commit comments