scp: return directory entries from the Zephyr entry walk

- The WOLFSSH_ZEPHYR branch of FindNextDirEntry() loops while the
  entry name is "." or "..", matching the POSIX and Windows
  branches, in place of while (1).
- tests/api.c gains test_wolfSSH_SCP_SendRecursiveEntry(), staging a
  directory holding one file and driving three
  WOLFSSH_SCP_RECURSIVE_REQUEST calls through wsScpSendCallback(),
  checking the entry name, size, and bytes placed in buf.  dirPath
  is rooted at CONFIG_WOLFSSH_SFTP_DEFAULT_DIR under WOLFSSH_ZEPHYR
  and at "./scp_recur_entry" otherwise.
- The test is gated on WOLFSSH_SCP, with WOLFSSH_SCP_USER_CALLBACKS,
  NO_FILESYSTEM and NO_WOLFSSH_DIR unset, carries an empty stub
  otherwise, and is called from wolfSSH_ApiTest().
- scpStageRecurFile() writes that fixture file.

Issue: F-13315
pull/1247/head
Yosuke Shimizu 2026-09-08 15:43:53 +09:00 committed by John Safranek
parent d3ad9fa5ed
commit c039486e52
2 changed files with 99 additions and 1 deletions

View File

@ -3058,7 +3058,10 @@ static int FindNextDirEntry(void *fs, ScpSendCtx* ctx)
return WS_FATAL_ERROR;
if (ctx->entry.name[0] == 0) /* Reached end-of-dir */
return WS_NEXT_ERROR;
} while (1);
} while (((WSTRLEN(ctx->entry.name) == 1) &&
(WSTRNCMP(ctx->entry.name, ".", 1) == 0)) ||
((WSTRLEN(ctx->entry.name) == 2) &&
(WSTRNCMP(ctx->entry.name, "..", 2) == 0)));
#else
do {
ctx->entry = WREADDIR(fs, &ctx->currentDir->dir);

View File

@ -2755,6 +2755,100 @@ static void test_wolfSSH_SCP_SendSymlinkReject(void)
static void test_wolfSSH_SCP_SendSymlinkReject(void) { ; }
#endif
#if defined(WOLFSSH_SCP) && !defined(WOLFSSH_SCP_USER_CALLBACKS) && \
!defined(NO_FILESYSTEM) && !defined(NO_WOLFSSH_DIR)
static int scpStageRecurFile(const char* name, const byte* buf, word32 sz)
{
WFILE* fp = NULL;
int ret = 0;
if (WFOPEN(NULL, &fp, name, "wb") != 0 || fp == NULL)
return -1;
if (WFWRITE(NULL, buf, 1, sz, fp) != sz)
ret = -1;
WFCLOSE(NULL, fp);
return ret;
}
/* A recursive send must hand back the directory's entries instead of going
* straight to the final exit. The example client cannot issue "scp -r -f",
* so drive the walk through wsScpSendCallback() at the callback level. */
static void test_wolfSSH_SCP_SendRecursiveEntry(void)
{
WOLFSSH_CTX* ctx = NULL;
WOLFSSH* ssh = NULL;
ScpSendCtx sendCtx;
char fileName[DEFAULT_SCP_FILE_NAME_SZ];
char filePath[DEFAULT_SCP_FILE_NAME_SZ];
char entryName[] = "a.txt";
#ifdef WOLFSSH_ZEPHYR
char dirPath[] = CONFIG_WOLFSSH_SFTP_DEFAULT_DIR "/scp_recur_entry";
#else
char dirPath[] = "./scp_recur_entry";
#endif
byte data[64];
byte buf[256];
word64 mTime = 0;
word64 aTime = 0;
word32 totalSz = 0;
word32 i;
int fileMode = 0;
for (i = 0; i < (word32)sizeof(data); i++)
data[i] = (byte)((i * 7 + 3) & 0xff);
WSNPRINTF(filePath, sizeof(filePath), "%s/%s", dirPath, entryName);
/* full teardown first: a run that aborted mid-test leaves these behind,
* and then WMKDIR below fails with EEXIST, masking the real failure with
* a setup error */
WREMOVE(NULL, filePath);
WRMDIR(NULL, dirPath);
AssertIntEQ(WMKDIR(NULL, dirPath, 0700), 0);
AssertIntEQ(scpStageRecurFile(filePath, data, sizeof(data)), 0);
AssertNotNull(ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL));
AssertNotNull(ssh = wolfSSH_new(ctx));
WMEMSET(&sendCtx, 0, sizeof(sendCtx));
WMEMSET(fileName, 0, sizeof(fileName));
/* the first call opens the root and reports the directory itself */
AssertIntEQ(wsScpSendCallback(ssh, WOLFSSH_SCP_RECURSIVE_REQUEST,
dirPath, fileName, (word32)sizeof(fileName), &mTime, &aTime,
&fileMode, 0, &totalSz, buf, (word32)sizeof(buf), &sendCtx),
WS_SCP_ENTER_DIR);
/* the second call must produce the staged entry, skipping "." and ".." to
* reach it; the return is the number of file bytes placed in buf */
AssertIntEQ(wsScpSendCallback(ssh, WOLFSSH_SCP_RECURSIVE_REQUEST,
dirPath, fileName, (word32)sizeof(fileName), &mTime, &aTime,
&fileMode, 0, &totalSz, buf, (word32)sizeof(buf), &sendCtx),
(int)sizeof(data));
AssertStrEQ(fileName, entryName);
AssertIntEQ(totalSz, (word32)sizeof(data));
AssertIntEQ(XMEMCMP(buf, data, sizeof(data)), 0);
/* the third call exhausts the directory and pops the only stack entry */
AssertIntEQ(wsScpSendCallback(ssh, WOLFSSH_SCP_RECURSIVE_REQUEST,
dirPath, fileName, (word32)sizeof(fileName), &mTime, &aTime,
&fileMode, 0, &totalSz, buf, (word32)sizeof(buf), &sendCtx),
WS_SCP_EXIT_DIR_FINAL);
AssertNull(sendCtx.currentDir);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
WREMOVE(NULL, filePath);
WRMDIR(NULL, dirPath);
}
#else
static void test_wolfSSH_SCP_SendRecursiveEntry(void) { ; }
#endif
#ifdef WOLFSSH_AGENT
/* Room for an add-identity message carrying a 3072-bit RSA key. */
#define AGENT_TEST_BUF_SZ 2048
@ -8143,6 +8237,7 @@ int wolfSSH_ApiTest(int argc, char** argv)
/* SCP tests */
test_wolfSSH_SCP_CB();
test_wolfSSH_SCP_SendSymlinkReject();
test_wolfSSH_SCP_SendRecursiveEntry();
test_wolfSSH_SCP_ReKey();
test_wolfSSH_SCP_ReKey_NonBlock();
test_wolfSSH_SCP_ReKey_ToServer();