From: Tobias Brunner Date: Mon, 1 Feb 2021 13:35:09 +0000 (+0100) Subject: path: Also accept / as directory separator on Windows X-Git-Tag: 5.9.2dr2~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e6a6fc33b6ff13f62f167013fb50149fdf239ef6;p=thirdparty%2Fstrongswan.git path: Also accept / as directory separator on Windows This adds helper functions to determine the first or last directory separator in a string and to check if a given character is a separator. Paths starting with a separator are now also considered absolute on Windows as these are rooted at the current drive. Note that it's fine to use DIRECTORY_SEPARATOR when combining strings as Windows API calls accept both forward and backward slashes as separators. Co-authored-by: MichaƂ Skalski References #3684. --- diff --git a/src/libimcv/plugins/imv_attestation/attest_db.c b/src/libimcv/plugins/imv_attestation/attest_db.c index bc435df7f8..1157a17c3d 100644 --- a/src/libimcv/plugins/imv_attestation/attest_db.c +++ b/src/libimcv/plugins/imv_attestation/attest_db.c @@ -337,7 +337,7 @@ METHOD(attest_db_t, set_directory, bool, /* remove trailing '/' or '\' character if not root directory */ len = strlen(dir); - if (len > 1 && dir[len-1] == DIRECTORY_SEPARATOR[0]) + if (len > 1 && path_is_separator(dir[len-1])) { dir[len-1] = '\0'; } diff --git a/src/libstrongswan/collections/enumerator.c b/src/libstrongswan/collections/enumerator.c index a663d52291..1dd11189b0 100644 --- a/src/libstrongswan/collections/enumerator.c +++ b/src/libstrongswan/collections/enumerator.c @@ -173,9 +173,9 @@ enumerator_t* enumerator_create_directory(const char *path) return NULL; } /* append a '/' if not already done */ - if (this->full[len-1] != '/') + if (!path_is_separator(this->full[len-1])) { - this->full[len++] = '/'; + this->full[len++] = DIRECTORY_SEPARATOR[0]; this->full[len] = '\0'; } this->full_end = &this->full[len]; diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index fd7cb647ae..ef2619327f 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -766,6 +766,97 @@ START_TEST(test_strreplace) } END_TEST +/******************************************************************************* + * path_first/last_separator + */ + +static struct { + char *path; + int len; + int first; + int last; +} separator_data[] = { + {NULL, -1, -1, -1}, + {"", -1, -1, -1}, + {".", -1, -1, -1}, + {"..", -1, -1, -1}, +#ifdef WIN32 + {"C:\\", -1, 2, 2}, + {"C:/", -1, 2, 2}, + {"X:\\\\", -1, 2, 3}, + {"d:\\f", -1, 2, 2}, + {"d:\\f", 2, -1, -1}, + {"C:\\foo\\", -1, 2, 6}, + {"foo\\bar", -1, 3, 3}, + {"foo\\\\bar", -1, 3, 4}, + {"\\foo\\bar", -1, 0, 4}, + {"\\\\foo\\bar", -1, 0, 5}, + {"\\\\foo\\bar", 4, 0, 1}, + {"foo\\bar/baz", -1, 3, 7}, +#endif /* WIN32 */ + {"/", -1, 0, 0}, + {"//", -1, 0, 1}, + {"foo", -1, -1, -1}, + {"f/", -1, 1, 1}, + {"foo/", -1, 3, 3}, + {"foo/", 2, -1, -1}, + {"foo//", -1, 3, 4}, + {"/foo", -1, 0, 0}, + {"/foo/", -1, 0, 4}, + {"/foo/", 3, 0, 0}, + {"//foo/", -1, 0, 5}, + {"foo/bar", -1, 3, 3}, + {"foo/bar", 1, -1, -1}, + {"foo/bar", 2, -1, -1}, + {"foo/bar", 3, -1, -1}, + {"foo/bar", 4, 3, 3}, + {"foo/bar", 5, 3, 3}, + {"foo//bar", -1, 3, 4}, + {"/foo/bar", -1, 0, 4}, + {"/foo/bar/", -1, 0, 8}, + {"/foo/bar/", 0, -1, -1}, + {"/foo/bar/", 1, 0, 0}, + {"/foo/bar/", 2, 0, 0}, + {"/foo/bar/", 3, 0, 0}, + {"/foo/bar/", 4, 0, 0}, + {"/foo/bar/", 5, 0, 4}, + {"/foo/bar/", 7, 0, 4}, + {"/foo/bar/", 8, 0, 4}, + {"/foo/bar/", 9, 0, 8}, +}; + +START_TEST(test_path_first_separator) +{ + char *pos; + + pos = path_first_separator(separator_data[_i].path, separator_data[_i].len); + if (separator_data[_i].first >= 0) + { + ck_assert_int_eq(pos-separator_data[_i].path, separator_data[_i].first); + } + else + { + ck_assert(!pos); + } +} +END_TEST + +START_TEST(test_path_last_separator) +{ + char *pos; + + pos = path_last_separator(separator_data[_i].path, separator_data[_i].len); + if (separator_data[_i].last >= 0) + { + ck_assert_int_eq(pos-separator_data[_i].path, separator_data[_i].last); + } + else + { + ck_assert(!pos); + } +} +END_TEST + /******************************************************************************* * path_dirname/basename/absolute */ @@ -782,6 +873,7 @@ static struct { {"..", ".", "..", FALSE}, #ifdef WIN32 {"C:\\", "C:", "C:", TRUE}, + {"C:/", "C:", "C:", TRUE}, {"X:\\\\", "X:", "X:", TRUE}, {"foo", ".", "foo", FALSE}, {"f\\", ".", "f", FALSE}, @@ -789,16 +881,20 @@ static struct { {"foo\\\\", ".", "foo", FALSE}, {"d:\\f", "d:", "f", TRUE}, {"C:\\f\\", "C:", "f", TRUE}, + {"C:\\f\\", "C:", "f", TRUE}, {"C:\\foo", "C:", "foo", TRUE}, {"C:\\foo\\", "C:", "foo", TRUE}, + {"C:\\foo/", "C:", "foo", TRUE}, {"foo\\bar", "foo", "bar", FALSE}, {"foo\\\\bar", "foo", "bar", FALSE}, {"C:\\foo\\bar", "C:\\foo", "bar", TRUE}, {"C:\\foo\\bar\\", "C:\\foo", "bar", TRUE}, {"C:\\foo\\bar\\baz", "C:\\foo\\bar", "baz", TRUE}, - {"\\foo\\bar", "\\foo", "bar", FALSE}, + {"C:\\foo/bar\\baz", "C:\\foo/bar", "baz", TRUE}, + {"C:/foo/bar/baz", "C:/foo/bar", "baz", TRUE}, + {"\\foo\\bar", "\\foo", "bar", TRUE}, {"\\\\foo\\bar", "\\\\foo", "bar", TRUE}, -#else /* !WIN32 */ +#endif /* WIN32 */ {"/", "/", "/", TRUE}, {"//", "/", "/", TRUE}, {"foo", ".", "foo", FALSE}, @@ -815,7 +911,6 @@ static struct { {"/foo/bar", "/foo", "bar", TRUE}, {"/foo/bar/", "/foo", "bar", TRUE}, {"/foo/bar/baz", "/foo/bar", "baz", TRUE}, -#endif }; START_TEST(test_path_dirname) @@ -1207,6 +1302,11 @@ Suite *utils_suite_create() tcase_add_loop_test(tc, test_strreplace, 0, countof(strreplace_data)); suite_add_tcase(s, tc); + tc = tcase_create("path_first/last_separator"); + tcase_add_loop_test(tc, test_path_first_separator, 0, countof(separator_data)); + tcase_add_loop_test(tc, test_path_last_separator, 0, countof(separator_data)); + suite_add_tcase(s, tc); + tc = tcase_create("path_dirname"); tcase_add_loop_test(tc, test_path_dirname, 0, countof(path_data)); suite_add_tcase(s, tc); diff --git a/src/libstrongswan/utils/utils/path.c b/src/libstrongswan/utils/utils/path.c index d964c70cc4..fc7cb37b93 100644 --- a/src/libstrongswan/utils/utils/path.c +++ b/src/libstrongswan/utils/utils/path.c @@ -24,22 +24,68 @@ #include #include -/** - * Described in header. +/* + * Described in header */ -char* path_dirname(const char *path) +char *path_first_separator(const char *path, int len) +{ + if (!path) + { + return NULL; + } + if (len < 0) + { + len = strlen(path); + } + for (; len; path++, len--) + { + if (path_is_separator(*path)) + { + return (char*)path; + } + } + return NULL; +} + +/* + * Described in header + */ +char *path_last_separator(const char *path, int len) +{ + if (!path) + { + return NULL; + } + if (len < 0) + { + len = strlen(path); + } + while (len) + { + if (path_is_separator(path[--len])) + { + return (char*)&path[len]; + } + } + return NULL; +} + +/* + * Described in header + */ +char *path_dirname(const char *path) { char *pos; - pos = path ? strrchr(path, DIRECTORY_SEPARATOR[0]) : NULL; + pos = path_last_separator(path, -1); if (pos && !pos[1]) - { /* if path ends with slashes we have to look beyond them */ - while (pos > path && *pos == DIRECTORY_SEPARATOR[0]) - { /* skip trailing slashes */ + { /* if path ends with separators, we have to look beyond them */ + while (pos > path && path_is_separator(*pos)) + { /* skip trailing separators */ pos--; } - pos = memrchr(path, DIRECTORY_SEPARATOR[0], pos - path + 1); + pos = path_last_separator(path, pos - path + 1); } if (!pos) { @@ -54,17 +100,17 @@ char* path_dirname(const char *path) #endif return strdup("."); } - while (pos > path && *pos == DIRECTORY_SEPARATOR[0]) - { /* skip superfluous slashes */ + while (pos > path && path_is_separator(*pos)) + { /* skip superfluous separators */ pos--; } return strndup(path, pos - path + 1); } -/** - * Described in header. +/* + * Described in header */ -char* path_basename(const char *path) +char *path_basename(const char *path) { char *pos, *trail = NULL; @@ -72,26 +118,26 @@ char* path_basename(const char *path) { return strdup("."); } - pos = strrchr(path, DIRECTORY_SEPARATOR[0]); + pos = path_last_separator(path, -1); if (pos && !pos[1]) - { /* if path ends with slashes we have to look beyond them */ - while (pos > path && *pos == DIRECTORY_SEPARATOR[0]) - { /* skip trailing slashes */ + { /* if path ends with separators, we have to look beyond them */ + while (pos > path && path_is_separator(*pos)) + { /* skip trailing separators */ pos--; } - if (pos == path && *pos == DIRECTORY_SEPARATOR[0]) - { /* contains only slashes */ - return strdup(DIRECTORY_SEPARATOR); + if (pos == path && path_is_separator(*pos)) + { /* contains only separators */ + return strndup(pos, 1); } trail = pos + 1; - pos = memrchr(path, DIRECTORY_SEPARATOR[0], trail - path); + pos = path_last_separator(path, trail - path); } pos = pos ? pos + 1 : (char*)path; return trail ? strndup(pos, trail - pos) : strdup(pos); } -/** - * Described in header. +/* + * Described in header */ bool path_absolute(const char *path) { @@ -108,22 +154,21 @@ bool path_absolute(const char *path) { /* drive letter */ return TRUE; } -#else /* !WIN32 */ - if (path[0] == DIRECTORY_SEPARATOR[0]) +#endif /* WIN32 */ + if (path_is_separator(path[0])) { return TRUE; } -#endif return FALSE; } -/** - * Described in header. +/* + * Described in header */ bool mkdir_p(const char *path, mode_t mode) { int len; - char *pos, full[PATH_MAX]; + char *pos, sep, full[PATH_MAX]; pos = full; if (!path || *path == '\0') { @@ -135,19 +180,20 @@ bool mkdir_p(const char *path, mode_t mode) DBG1(DBG_LIB, "path string %s too long", path); return FALSE; } - /* ensure that the path ends with a '/' */ - if (full[len-1] != '/') + /* ensure that the path ends with a separator */ + if (!path_is_separator(full[len-1])) { - full[len++] = '/'; + full[len++] = DIRECTORY_SEPARATOR[0]; full[len] = '\0'; } - /* skip '/' at the beginning */ - while (*pos == '/') + /* skip separators at the beginning */ + while (path_is_separator(*pos)) { pos++; } - while ((pos = strchr(pos, '/'))) + while ((pos = path_first_separator(pos, -1))) { + sep = *pos; *pos = '\0'; if (access(full, F_OK) < 0) { @@ -161,7 +207,7 @@ bool mkdir_p(const char *path, mode_t mode) return FALSE; } } - *pos = '/'; + *pos = sep; pos++; } return TRUE; diff --git a/src/libstrongswan/utils/utils/path.h b/src/libstrongswan/utils/utils/path.h index b72bdaf425..de00535ede 100644 --- a/src/libstrongswan/utils/utils/path.h +++ b/src/libstrongswan/utils/utils/path.h @@ -27,10 +27,47 @@ */ #ifdef WIN32 # define DIRECTORY_SEPARATOR "\\" +/* the Windows API also accepts / as separator */ +# define DIRECTORY_SEPARATOR_ALT "/" #else # define DIRECTORY_SEPARATOR "/" #endif +/** + * Check if the given character is a directory separator. + * + * @param c character + * @return TRUE if the character is a directory separator + */ +static inline bool path_is_separator(char c) +{ +#ifdef WIN32 + return c == DIRECTORY_SEPARATOR[0] || c == DIRECTORY_SEPARATOR_ALT[0]; +#else + return c == DIRECTORY_SEPARATOR[0]; +#endif +} + +/** + * Returns a pointer to the first occurrence of a directory separator in the + * given string (optionally limited to a given length). + * + * @param path path to search + * @param len optional length to search, -1 for the full string + * @return pointer to separator, or NULL if not found + */ +char *path_first_separator(const char *path, int len); + +/** + * Returns a pointer to the last occurrence of a directory separator in the + * given string (optionally limited to a given length). + * + * @param path path to search + * @param len optional length to search, -1 for the full string + * @return pointer to separator, or NULL if not found + */ +char *path_last_separator(const char *path, int len); + /** * Like dirname(3) returns the directory part of the given null-terminated * pathname, up to but not including the final '/' (or '.' if no '/' is found).