@@ -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
@@ -6040,6 +6058,133 @@ static void test_wolfSSH_SFTP_PutResume(void)
60406058}
60416059
60426060
6061+ /* A resumed get must keep the bytes already at the local destination, and
6062+ * must only resume onto one whose size matches the saved offset. The
6063+ * echoserver shares this process, so the "remote" file is local. */
6064+ static void test_wolfSSH_SFTP_GetResume (void )
6065+ {
6066+ /* staging both files needs the hosted fopen()/getcwd() wrappers */
6067+ #if !defined(NO_FILESYSTEM ) && !defined(WOLFSSH_USER_FILESYSTEM ) && \
6068+ !defined(WOLFSSH_ZEPHYR )
6069+ func_args ser ;
6070+ tcp_ready ready ;
6071+ int argsCount ;
6072+ WS_SOCKET_T clientFd ;
6073+
6074+ const char * args [10 ];
6075+ WOLFSSH_CTX * ctx = NULL ;
6076+ WOLFSSH * ssh = NULL ;
6077+
6078+ THREAD_TYPE serThread ;
6079+
6080+ byte src [SFTP_PUT_RESUME_SZ ];
6081+ byte stale [SFTP_PUT_RESUME_SZ + SFTP_PUT_RESUME_OFST ];
6082+ byte expect [SFTP_PUT_RESUME_SZ ];
6083+ word32 ofst [2 ];
6084+ char srcName [] = "wolfssh_12547_src.tmp" ;
6085+ char dstName [] = "wolfssh_12547_dst.tmp" ;
6086+
6087+ WMEMSET (& ser , 0 , sizeof (func_args ));
6088+
6089+ argsCount = 0 ;
6090+ args [argsCount ++ ] = "." ;
6091+ args [argsCount ++ ] = "-1" ;
6092+ args [argsCount ++ ] = "-p" ;
6093+ args [argsCount ++ ] = "0" ;
6094+ ser .argv = (char * * )args ;
6095+ ser .argc = argsCount ;
6096+ ser .signal = & ready ;
6097+ InitTcpReady (ser .signal );
6098+ ThreadStart (echoserver_test , (void * )& ser , & serThread );
6099+ WaitTcpReady (& ready );
6100+
6101+ sftp_client_connect (& ctx , & ssh , ready .port );
6102+ AssertNotNull (ctx );
6103+ AssertNotNull (ssh );
6104+
6105+ sftpPutFillPattern (src , (word32 )sizeof (src ));
6106+ WMEMSET (stale , 0xFF , sizeof (stale ));
6107+ AssertIntEQ (sftpPutWriteFile (srcName , src , (word32 )sizeof (src )), 0 );
6108+
6109+ /* the saved offset matches the destination, so the download picks up
6110+ * where it left off. Staging a prefix unlike the source, and expecting it
6111+ * back untouched, is what separates a resume from a full re-download. */
6112+ AssertIntEQ (sftpPutWriteFile (dstName , stale , SFTP_PUT_RESUME_OFST ), 0 );
6113+ WMEMCPY (expect , stale , SFTP_PUT_RESUME_OFST );
6114+ WMEMCPY (expect + SFTP_PUT_RESUME_OFST , src + SFTP_PUT_RESUME_OFST ,
6115+ sizeof (expect ) - SFTP_PUT_RESUME_OFST );
6116+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6117+ ofst [1 ] = 0 ;
6118+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6119+ WS_SUCCESS );
6120+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6121+ AssertIntEQ (sftpPutFileMatches (dstName , expect , (word32 )sizeof (expect )),
6122+ 0 );
6123+
6124+ /* the destination is gone, so the whole file is fetched again */
6125+ WREMOVE (NULL , dstName );
6126+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6127+ ofst [1 ] = 0 ;
6128+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6129+ WS_SUCCESS );
6130+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6131+ AssertIntEQ (sftpPutFileMatches (dstName , src , (word32 )sizeof (src )), 0 );
6132+
6133+ /* the destination is shorter than the saved offset, so the whole file is
6134+ * fetched again */
6135+ AssertIntEQ (sftpPutWriteFile (dstName , stale , SFTP_PUT_RESUME_OFST / 2 ), 0 );
6136+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6137+ ofst [1 ] = 0 ;
6138+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6139+ WS_SUCCESS );
6140+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6141+ AssertIntEQ (sftpPutFileMatches (dstName , src , (word32 )sizeof (src )), 0 );
6142+
6143+ /* the destination is longer than the saved offset, so the whole file is
6144+ * fetched again */
6145+ AssertIntEQ (sftpPutWriteFile (dstName , stale , SFTP_PUT_RESUME_OFST * 2 ), 0 );
6146+ ofst [0 ] = SFTP_PUT_RESUME_OFST ;
6147+ ofst [1 ] = 0 ;
6148+ AssertIntEQ (wolfSSH_SFTP_SaveOfst (ssh , srcName , dstName , ofst ),
6149+ WS_SUCCESS );
6150+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 1 ), WS_SUCCESS );
6151+ AssertIntEQ (sftpPutFileMatches (dstName , src , (word32 )sizeof (src )), 0 );
6152+
6153+ /* a plain get still truncates a longer destination */
6154+ AssertIntEQ (sftpPutWriteFile (dstName , stale , (word32 )sizeof (stale )), 0 );
6155+ AssertIntEQ (sftpGetToCompletion (ssh , srcName , dstName , 0 ), WS_SUCCESS );
6156+ AssertIntEQ (sftpPutFileMatches (dstName , src , (word32 )sizeof (src )), 0 );
6157+
6158+ WREMOVE (NULL , dstName );
6159+ WREMOVE (NULL , srcName );
6160+
6161+ /* take care of re-keying state before shutdown call */
6162+ while (wolfSSH_get_error (ssh ) == WS_REKEYING ) {
6163+ wolfSSH_worker (ssh , NULL );
6164+ }
6165+
6166+ argsCount = AbsorbBenignReset (ssh , wolfSSH_shutdown (ssh ));
6167+ #if DEFAULT_HIGHWATER_MARK < 8000
6168+ if (argsCount == WS_REKEYING ) {
6169+ argsCount = WS_SUCCESS ;
6170+ }
6171+ #endif
6172+ AssertIntEQ (argsCount , WS_SUCCESS );
6173+
6174+ clientFd = wolfSSH_get_fd (ssh );
6175+ WCLOSESOCKET (clientFd );
6176+
6177+ wolfSSH_free (ssh );
6178+ wolfSSH_CTX_free (ctx );
6179+ #ifdef WOLFSSH_ZEPHYR
6180+ k_sleep (Z_TIMEOUT_TICKS (100 ));
6181+ #endif
6182+ ThreadJoin (serThread );
6183+ FreeTcpReady (& ready );
6184+ #endif /* !NO_FILESYSTEM && !WOLFSSH_USER_FILESYSTEM && !WOLFSSH_ZEPHYR */
6185+ }
6186+
6187+
60436188#else /* WOLFSSH_SFTP && !NO_WOLFSSH_CLIENT && !SINGLE_THREADED */
60446189static void test_wolfSSH_SFTP_SendReadPacket (void ) { ; }
60456190static void test_wolfSSH_SFTP_PartialSend (void ) { ; }
@@ -6051,6 +6196,7 @@ static void test_wolfSSH_SFTP_SetConfinePath(void) { ; }
60516196static void test_wolfSSH_SFTP_SetDefaultPath (void ) { ; }
60526197static void test_wolfSSH_SFTP_SaveOfst (void ) { ; }
60536198static void test_wolfSSH_SFTP_PutResume (void ) { ; }
6199+ static void test_wolfSSH_SFTP_GetResume (void ) { ; }
60546200#endif /* WOLFSSH_SFTP && !NO_WOLFSSH_CLIENT && !SINGLE_THREADED */
60556201
60566202
@@ -7999,6 +8145,7 @@ int wolfSSH_ApiTest(int argc, char** argv)
79998145 test_wolfSSH_SFTP_SetDefaultPath ();
80008146 test_wolfSSH_SFTP_SaveOfst ();
80018147 test_wolfSSH_SFTP_PutResume ();
8148+ test_wolfSSH_SFTP_GetResume ();
80028149
80038150 /* Either SCP or SFTP */
80048151 test_wolfSSH_RealPath ();
0 commit comments