From a48926b0dd65d8ab7abb248844513afddbb63d24 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Fri, 16 Sep 2022 12:13:09 -0700 Subject: [PATCH] SFTP List 1. Update wolfSSH_SFTPNAME_readdir() to have a special case getting the drive letters at root in Windows. 2. The Win32 APIs can use forward slashes, but cannot have one at the beginning of the path before the drive letter. Add a trim function to remove the leading slash in the SFTP paths. 3. Update echoserver to always set the default SFTP path to either the provided command line option or to the CWD for the echoserver run. 4. Get the RealPath for the default path 5. Tweak wolfSSH_RealPath() to handle some Windows cases. 6. Added more test cases. --- examples/echoserver/echoserver.c | 45 +++++++++++++++++++++++++++----- src/port.c | 24 +++++++++++++++++ src/ssh.c | 19 +++++++++----- src/wolfsftp.c | 33 ++++++++++++++--------- tests/api.c | 6 +++++ 5 files changed, 102 insertions(+), 25 deletions(-) diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 58819e0b..4b0522a0 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -2030,6 +2030,42 @@ static int wsUserAuth(byte authType, } +#ifdef WOLFSSH_SFTP +static int SetDefaultSftpPath(WOLFSSH* ssh, const char* defaultSftpPath) +{ + char path[WOLFSSH_MAX_FILENAME]; + char realPath[WOLFSSH_MAX_FILENAME]; + int ret = 0; + + if (defaultSftpPath == NULL) { + #ifdef USE_WINDOWS_API + if (GetCurrentDirectoryA(sizeof(path), path) == 0) { + ret = -1; + } + #else + if (getcwd(path, sizeof(path)) == NULL) { + ret = -1; + } + #endif + } + else { + WSTRNCPY(path, defaultSftpPath, sizeof(path)); + path[sizeof(path) - 1] = 0; + } + + if (ret == 0) { + path[sizeof(path) - 1] = 0; + wolfSSH_RealPath(NULL, path, realPath, sizeof(realPath)); + if (wolfSSH_SFTP_SetDefaultPath(ssh, realPath) != WS_SUCCESS) { + ret = -1; + } + } + + return ret; +} +#endif + + static void ShowUsage(void) { printf("echoserver %s\n", LIBWOLFSSH_VERSION_STRING); @@ -2451,12 +2487,9 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) } #ifdef WOLFSSH_SFTP - if (defaultSftpPath) { - if (wolfSSH_SFTP_SetDefaultPath(ssh, defaultSftpPath) - != WS_SUCCESS) { - fprintf(stderr, "Couldn't store default sftp path.\n"); - WEXIT(EXIT_FAILURE); - } + if (SetDefaultSftpPath(ssh, defaultSftpPath) != 0) { + fprintf(stderr, "Couldn't store default sftp path.\n"); + WEXIT(EXIT_FAILURE); } #endif diff --git a/src/port.c b/src/port.c index 88ed5d87..a60df81a 100644 --- a/src/port.c +++ b/src/port.c @@ -166,6 +166,15 @@ int wfopen(WFILE** f, const char* filename, const char* mode) #if defined(USE_WINDOWS_API) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) +static const char* TrimFileName(const char* f, size_t* fSz) +{ + if (f != NULL && fSz != NULL && *fSz >= 3 && f[0] == '/' && f[2] == ':') { + f++; + *fSz--; + } + return f; +} + void* WS_CreateFileA(const char* fileName, unsigned long desiredAccess, unsigned long shareMode, unsigned long creationDisposition, unsigned long flags, void* heap) @@ -178,6 +187,8 @@ void* WS_CreateFileA(const char* fileName, unsigned long desiredAccess, errno_t error; fileNameSz = WSTRLEN(fileName); + fileName = TrimFileName(fileName, &fileNameSz); + error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0); if (error) return INVALID_HANDLE_VALUE; @@ -211,6 +222,8 @@ void* WS_FindFirstFileA(const char* fileName, errno_t error; fileNameSz = WSTRLEN(fileName); + fileName = TrimFileName(fileName, &fileNameSz); + error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0); if (error) return INVALID_HANDLE_VALUE; @@ -268,6 +281,8 @@ int WS_GetFileAttributesExA(const char* fileName, void* fileInfo, void* heap) errno_t error; fileNameSz = WSTRLEN(fileName); + fileName = TrimFileName(fileName, &fileNameSz); + error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0); if (error != 0) return 0; @@ -301,6 +316,8 @@ int WS_RemoveDirectoryA(const char* dirName, void* heap) errno_t error; dirNameSz = WSTRLEN(dirName); + dirName = TrimFileName(dirName, &dirNameSz); + error = mbstowcs_s(&unicodeDirNameSz, NULL, 0, dirName, 0); if (error != 0) return 0; @@ -333,6 +350,8 @@ int WS_CreateDirectoryA(const char* dirName, void* heap) errno_t error; dirNameSz = WSTRLEN(dirName); + dirName = TrimFileName(dirName, &dirNameSz); + error = mbstowcs_s(&unicodeDirNameSz, NULL, 0, dirName, 0); if (error != 0) return 0; @@ -368,6 +387,7 @@ int WS_MoveFileA(const char* oldName, const char* newName, void* heap) errno_t error; oldNameSz = WSTRLEN(oldName); + oldName = TrimFileName(oldName, &oldNameSz); error = mbstowcs_s(&unicodeOldNameSz, NULL, 0, oldName, 0); if (error != 0) @@ -382,6 +402,8 @@ int WS_MoveFileA(const char* oldName, const char* newName, void* heap) oldName, oldNameSz); newNameSz = WSTRLEN(newName); + newName = TrimFileName(newName, &newNameSz); + error = mbstowcs_s(&unicodeNewNameSz, NULL, 0, newName, 0); if (error != 0) return 0; @@ -417,6 +439,8 @@ int WS_DeleteFileA(const char* fileName, void* heap) errno_t error; fileNameSz = WSTRLEN(fileName); + fileName = TrimFileName(fileName, &fileNameSz); + error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0); if (error != 0) return 0; diff --git a/src/ssh.c b/src/ssh.c index 25a609c4..a27bf2c5 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -2274,6 +2274,11 @@ int wolfSSH_ChannelGetEof(WOLFSSH_CHANNEL* channel) #if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \ !defined(NO_WOLFSSH_SERVER) + +#define DELIM "/\\" +#define IS_DELIM(x) ((x) == '/' || (x) == '\\') +#define IS_WINPATH(x,y) ((x) > 1 && (y)[1] == ':') + /* * Paths starting with a slash are absolute, rooted at "/". Any path that * doesn't have a starting slash is assumed to be relative to the default @@ -2311,16 +2316,18 @@ int wolfSSH_RealPath(const char* defaultPath, char* in, WMEMSET(out, 0, outSz); inSz = (word32)WSTRLEN(in); - if ((inSz == 0 || in[0] != '/') && defaultPath != NULL){ - WSTRNCPY(out, defaultPath, outSz); - } - else { - out[0] = '/'; + out[0] = '/'; + if (inSz == 0 || (!IS_DELIM(in[0]) && !IS_WINPATH(inSz, in))) { + if (defaultPath != NULL) { + WSTRNCPY(out, defaultPath, outSz); + } } out[outSz - 1] = 0; curSz = (word32)WSTRLEN(out); - for (seg = WSTRTOK(in, "/", &tail); seg; seg = WSTRTOK(NULL, "/", &tail)) { + for (seg = WSTRTOK(in, DELIM, &tail); + seg; + seg = WSTRTOK(NULL, DELIM, &tail)) { segSz = (word32)WSTRLEN(seg); /* Try to match "." */ diff --git a/src/wolfsftp.c b/src/wolfsftp.c index 8bb3e4bd..98b46dc8 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -2265,8 +2265,10 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) int i; WMEMSET(ssh->driveList, 0, sizeof ssh->driveList); + ssh->driveListCount = 0; + drives = GetLogicalDrives(); - for (i = 0, mask = 1; i < 26; i++, mask <<= 1) { + for (i = 0, mask = 1; i < (sizeof ssh->driveList); i++, mask <<= 1) { if (drives & mask) { driveName[0] = 'A' + i; driveType = GetDriveTypeA(driveName); @@ -2649,7 +2651,7 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, char* dirName) { - int sz; + int sz, special = 0; HANDLE findHandle; char realFileName[MAX_PATH]; @@ -2658,13 +2660,13 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, } /* special case of getting drives at "/" */ - if (dirName[0] == "/" && dirName[1] == 0) { + if (dirName[0] == '/' && dirName[1] == 0) { if (ssh->driveIdx >= ssh->driveListCount) return WS_FATAL_ERROR; realFileName[0] = ssh->driveList[ssh->driveIdx++]; realFileName[1] = ':'; - realFileName[2] = '/'; - realFileName[3] = '\0'; + realFileName[2] = '\0'; + special = 1; } else if (*dir == INVALID_HANDLE_VALUE) { char name[MAX_PATH]; @@ -2714,16 +2716,21 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, return WS_MEMORY_E; } buf[0] = '\0'; - WSTRNCAT(buf, dirName, bufSz + 1); - tmpSz = (int)WSTRLEN(buf); + if (!special) { + WSTRNCAT(buf, dirName, bufSz + 1); + tmpSz = (int)WSTRLEN(buf); - /* add delimiter between path and file/dir name */ - if (tmpSz + 1 < bufSz) { - buf[tmpSz] = WS_DELIM; - buf[tmpSz + 1] = '\0'; + /* add delimiter between path and file/dir name */ + if (tmpSz + 1 < bufSz) { + buf[tmpSz] = WS_DELIM; + buf[tmpSz + 1] = '\0'; + } + WSTRNCAT(buf, out->fName, bufSz + 1); + } + else { + WSTRNCAT(buf, realFileName, bufSz + 1); + WSTRNCAT(buf, "\\", bufSz + 1); } - WSTRNCAT(buf, out->fName, bufSz + 1); - if (SFTP_GetAttributes(ssh->fs, buf, &out->atrb, 0, ssh->ctx->heap) != WS_SUCCESS) { WLOG(WS_LOG_SFTP, "Unable to get attribute values for %s", diff --git a/tests/api.c b/tests/api.c index 70076989..ae90860d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1050,6 +1050,12 @@ struct RealPathTestCase realPathDefault[] = { { "./sample.jpg", "/C:/Users/fred/sample.jpg" }, { "./sample./other", "/C:/Users/fred/sample./other" }, { "./sample.dir/other", "/C:/Users/fred/sample.dir/other" }, + { "\\C:\\Users\\fred\\Documents\\junk.txt", + "/C:/Users/fred/Documents/junk.txt" }, + { "C:\\Users\\fred\\Documents\\junk.txt", + "/C:/Users/fred/Documents/junk.txt" }, + { "/C:\\Users\\fred/Documents\\junk.txt", + "/C:/Users/fred/Documents/junk.txt" }, }; struct RealPathTestCase realPathNull[] = {