Skip to content

Commit 99fd7c9

Browse files
Merge pull request #440 from ejohnstown/real-path
SFTP RealPath
2 parents 25afeba + 185a730 commit 99fd7c9

3 files changed

Lines changed: 158 additions & 0 deletions

File tree

src/ssh.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,6 +2194,97 @@ int wolfSSH_ChannelGetEof(WOLFSSH_CHANNEL* channel)
21942194
}
21952195

21962196

2197+
#if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \
2198+
!defined(NO_WOLFSSH_SERVER)
2199+
/*
2200+
* Paths starting with a slash are absolute, rooted at root. Any path that
2201+
* doesn't have a starting slash is assumed to be relative to the current
2202+
* path. If the path is empty, return the default path.
2203+
*
2204+
* The path "/." is stripped out. The path "/.." strips out the previous
2205+
* path value. The root path, "/", is always present.
2206+
*
2207+
* Example: "/home/fred/frob/frizz/../../../barney/bar/baz/./././../.."
2208+
* will return "/home/barney". "/../.." will return "/". "." will return
2209+
* currentPath.
2210+
*
2211+
* Note, this function does not care about OS and filesystem issues. The
2212+
* SFTP protocol describes how paths are handled in SFTP. Specialized
2213+
* behaviors are handled when actually calling the OS functions. Paths
2214+
* are further massaged there. For example, the C: drive is treated as
2215+
* the path "/C:", and is a directory like any other.
2216+
*
2217+
* @param currentPath RealPath of the current working directory
2218+
* @param defaultPath RealPath of the default directory, usually user's
2219+
* @param in requested new path
2220+
* @param out output of real path cleanup
2221+
* @param outSz size in bytes of buffer 'out'
2222+
* @return WS_SUCCESS, WS_BAD_ARGUMENT, or WS_INVALID_PATH_E
2223+
*/
2224+
int wolfSSH_RealPath(const char* currentPath, const char* defaultPath,
2225+
char* in, char* out, word32 outSz)
2226+
{
2227+
char* tail = NULL;
2228+
char* seg;
2229+
word32 inSz, segSz, curSz;
2230+
2231+
if (currentPath == NULL || defaultPath == NULL ||
2232+
in == NULL || out == NULL || outSz == 0)
2233+
return WS_BAD_ARGUMENT;
2234+
2235+
WMEMSET(out, 0, outSz);
2236+
inSz = (word32)WSTRLEN(in);
2237+
if (inSz == 0) {
2238+
WSTRNCPY(out, defaultPath, outSz);
2239+
}
2240+
else if (in[0] == '/') {
2241+
out[0] = '/';
2242+
}
2243+
else {
2244+
WSTRNCPY(out, currentPath, outSz);
2245+
}
2246+
out[outSz - 1] = 0;
2247+
curSz = (word32)WSTRLEN(out);
2248+
2249+
for (seg = WSTRTOK(in, "/", &tail); seg; seg = WSTRTOK(NULL, "/", &tail)) {
2250+
segSz = (word32)WSTRLEN(seg);
2251+
2252+
/* Try to match "." */
2253+
if (segSz == 1 && seg[0] == '.') {
2254+
/* Do nothing. Keep current directory. */
2255+
}
2256+
/* Try to match ".." */
2257+
else if (segSz == 2 && seg[0] == '.' && seg[1] == '.') {
2258+
char* prev = strrchr(out, '/');
2259+
2260+
if (prev != NULL) {
2261+
if (prev != out) {
2262+
prev[0] = 0;
2263+
curSz = (word32)WSTRLEN(out);
2264+
}
2265+
else {
2266+
/* preserve the root / */
2267+
prev[1] = 0;
2268+
curSz = 1;
2269+
}
2270+
}
2271+
}
2272+
/* Everything else is copied */
2273+
else {
2274+
if (curSz != 1) {
2275+
WSTRNCAT(out, "/", outSz - curSz);
2276+
curSz++;
2277+
}
2278+
WSTRNCAT(out, seg, outSz - curSz);
2279+
curSz += segSz;
2280+
}
2281+
}
2282+
2283+
return WS_SUCCESS;
2284+
}
2285+
#endif /* WOLFSSH_SFTP || WOLFSSH_SCP */
2286+
2287+
21972288
#ifdef WOLFSSH_SHOW_SIZES
21982289

21992290
void wolfSSH_ShowSizes(void)

tests/api.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,68 @@ static void test_wstrcat(void)
895895
}
896896

897897

898+
#if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \
899+
!defined(NO_WOLFSSH_SERVER)
900+
struct RealPathTestCase {
901+
const char* in;
902+
const char* exp;
903+
};
904+
905+
struct RealPathTestCase realPathTestCases[] = {
906+
{ ".", "/C:/Users/fred" },
907+
{ "", "/C:/Users/fred" },
908+
{ "/C:/Users/fred/..", "/C:/Users" },
909+
{ "..", "/C:/Users" },
910+
{ "../..", "/C:" },
911+
{ "../barney", "/C:/Users/barney" },
912+
{ "/C:/Users/..", "/C:" },
913+
{ "/C:/..", "/" },
914+
{ "/C:/../../../../../../../..", "/" },
915+
{ "/", "/" },
916+
{ "/C:/Users/fred/../..", "/C:" },
917+
{ "/C:/Users/fred/././././.", "/C:/Users/fred" },
918+
{ "/C:/Users/fred/../././..", "/C:" },
919+
{ "./.ssh", "/C:/Users/fred/.ssh" },
920+
{ "./.ssh/../foo", "/C:/Users/fred/foo" },
921+
{ "./.ssh/../foo", "/C:/Users/fred/foo" },
922+
{ "///home//////////fred///", "/home/fred" },
923+
{ "/home/C:/ok", "/home/C:/ok" },
924+
{ "/home/fred/frob/frizz/../../../barney/bar/baz/./././../..",
925+
"/home/barney" },
926+
};
927+
const char* defaultPath = "/C:/Users/fred";
928+
929+
static void test_wolfSSH_RealPath(void)
930+
{
931+
struct RealPathTestCase* tc;
932+
char testPath[128];
933+
char checkPath[128];
934+
word32 testCount =
935+
(sizeof realPathTestCases)/(sizeof(struct RealPathTestCase));
936+
word32 i;
937+
int err;
938+
939+
for (i = 0, tc = realPathTestCases; i < testCount; i++, tc++) {
940+
WSTRNCPY(testPath, tc->in, sizeof(testPath) - 1);
941+
testPath[sizeof(testPath) - 1] = 0;
942+
WMEMSET(checkPath, 0, sizeof checkPath);
943+
err = wolfSSH_RealPath(defaultPath, defaultPath, testPath,
944+
checkPath, sizeof checkPath);
945+
if (err || WSTRCMP(tc->exp, checkPath) != 0) {
946+
printf("RealPath failure (case %u: %d)\n"
947+
" defaultPath: %s\n"
948+
" input: %s\n"
949+
" expected: %s\n"
950+
" output: %s\n", i, err,
951+
defaultPath, tc->in, tc->exp, checkPath);
952+
}
953+
}
954+
}
955+
#else
956+
static void test_wolfSSH_RealPath(void) { ; }
957+
#endif
958+
959+
898960
int main(void)
899961
{
900962
AssertIntEQ(wolfSSH_Init(), WS_SUCCESS);
@@ -907,6 +969,7 @@ int main(void)
907969
test_wolfSSH_SetUsername();
908970
test_wolfSSH_ConvertConsole();
909971
test_wolfSSH_CTX_UsePrivateKey_buffer();
972+
test_wolfSSH_RealPath();
910973

911974
/* SCP tests */
912975
test_wolfSSH_SCP_CB();

wolfssh/ssh.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ enum WS_DisconnectReasonCodes {
321321
};
322322

323323

324+
WOLFSSH_API int wolfSSH_RealPath(const char* currentPath,
325+
const char* defaultPath, char* in, char* out, word32 outSz);
326+
327+
324328
WOLFSSH_API void wolfSSH_ShowSizes(void);
325329

326330

0 commit comments

Comments
 (0)