diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | src/bindfs.c | 4 | ||||
-rw-r--r-- | src/misc.c | 39 | ||||
-rw-r--r-- | src/misc.h | 4 | ||||
-rw-r--r-- | tests/internals/test_common.h | 2 | ||||
-rw-r--r-- | tests/internals/test_internals.c | 73 |
6 files changed, 119 insertions, 8 deletions
@@ -1,3 +1,8 @@ +2022-10-17 Martin Pärtel <martin dot partel at gmail dot com> + + * Fixed --resolve-symlinks when mountpoint name starts + with source dir name (issue #122, thanks @1-eric) + 2022-10-06 Martin Pärtel <martin dot partel at gmail dot com> * Basic OpenBSD support. The test suite does not pass yet. diff --git a/src/bindfs.c b/src/bindfs.c index 65384dc..263aa0e 100644 --- a/src/bindfs.c +++ b/src/bindfs.c @@ -393,10 +393,10 @@ static char *process_path(const char *path, bool resolve_symlinks) we want to be able to operate on broken symlinks. */ return strdup(path); } - } else if (strncmp(result, settings.mntdest, settings.mntdest_len) == 0) { + } else if (path_starts_with(result, settings.mntdest, settings.mntdest_len)) { /* Recursive call. We cannot handle this without deadlocking, especially in single-threaded mode. */ - DPRINTF("Denying recursive access to mountpoint `%s'", result); + DPRINTF("Denying recursive access to mountpoint \"%s\" at \"%s\"", settings.mntdest, result); free(result); errno = EPERM; return NULL; @@ -135,6 +135,45 @@ const char *my_dirname(char *path) } } +static const char* find_last_char_between(const char* start, const char* end, char ch) { + assert(start != NULL && end != NULL); + const char* p = end - 1; + while (p >= start) { + if (*p == ch) { + return p; + } + --p; + } + return NULL; +} + +bool path_starts_with(const char *path, const char* prefix, size_t prefix_len) +{ + size_t path_len = strlen(path); + while (prefix_len > 0 && prefix[prefix_len - 1] == '/') { + --prefix_len; + } + while (path_len > 0 && path[path_len - 1] == '/') { + --path_len; + } + + if (strncmp(path, prefix, prefix_len) == 0) { + // We still need to check that the last path component of + // 'path' does not simply start with the last path component of 'prefix'. + const char* prefix_slash = find_last_char_between(prefix, prefix + prefix_len, '/'); + const char* prefix_part = prefix_slash ? prefix_slash + 1 : prefix; + size_t prefix_part_len = (prefix + prefix_len - prefix_part); + + const char* path_part = path + (prefix_part - prefix); + const char* path_slash = strchr(path_part, '/'); + size_t path_part_len = path_slash ? path_slash - path_part : path_len - (path_part - path); + + return prefix_part_len == path_part_len; + } + + return false; +} + static char **dup_argv(int argc, const char * const *argv, struct arena *arena) { char **pointer_list = arena_malloc(arena, (argc + 1) * sizeof(char*)); @@ -53,6 +53,10 @@ const char *my_basename(const char *path); Otherwise, returns ".". */ const char *my_dirname(char *path); +/* Returns true if the initial path components of 'path' are the path components of 'prefix'. + Assumes 'strlen(prefix) == prefix_len'. */ +bool path_starts_with(const char *path, const char* prefix, size_t prefix_len); + /* Filters arguments in comma-separated lists prefixed by '-o'. * Allocates 'new_argv' and its strings, as well as some temporary data, into 'arena'. */ void filter_o_opts( diff --git a/tests/internals/test_common.h b/tests/internals/test_common.h index 160d9b9..1688add 100644 --- a/tests/internals/test_common.h +++ b/tests/internals/test_common.h @@ -7,7 +7,7 @@ extern int failures; -#define TEST_ASSERT(expr) do { if (!(expr)) { printf("Assertion failed: %s:%d: `%s'\n", __FILE__, __LINE__, #expr); failures++; } } while (0); +#define TEST_ASSERT(expr) do { if (!(expr)) { printf("Assertion failed: %s:%d: `%s'\n", __FILE__, __LINE__, #expr); ++failures; } } while (0); #define NEAR(a, b, eps) (fabs((a) - (b)) < (eps)) int run_suite(void (*suite)()); diff --git a/tests/internals/test_internals.c b/tests/internals/test_internals.c index 9fa84d0..53f9872 100644 --- a/tests/internals/test_internals.c +++ b/tests/internals/test_internals.c @@ -40,13 +40,21 @@ static void test_my_dirname(char *arg, const char *expected) const char *ret = my_dirname(arg); if (strcmp(ret, expected) != 0) { - printf("Expected my_dirname(`%s') to return `%s' but got `%s'\n", orig, expected, ret); - failures++; + printf("Expected my_dirname(\"%s\") to return \"%s\" but got \"%s\"\n", orig, expected, ret); + ++failures; } free(orig); } +static void test_path_starts_with(const char* path, const char* prefix, bool expected) +{ + if (path_starts_with(path, prefix, strlen(prefix)) != expected) { + printf("Expected path_starts_with(\"%s\", \"%s\") to return %d but got %d\n", path, prefix, (int)expected, (int)!expected); + ++failures; + } +} + static void my_dirname_suite() { char buf[256]; @@ -82,6 +90,60 @@ static void my_dirname_suite() test_my_dirname(buf, ".."); } +static void path_starts_with_suite() +{ + test_path_starts_with("/a/b/c", "/a/b", true); + test_path_starts_with("/a/b/c", "/a/b/", true); + test_path_starts_with("/a/b/c/", "/a/b/c/", true); + test_path_starts_with("/a/b/c", "/a/b/c/", true); + test_path_starts_with("/a/b/c", "/a/b/c", true); + test_path_starts_with("/a/b/c", "/a/b/c/d", false); + test_path_starts_with("/a/b/c/d", "/a/b/c", true); + test_path_starts_with("/a/x/c", "/a/b", false); + test_path_starts_with("/x/b/c", "/a/b", false); + test_path_starts_with("/a", "/a/b", false); + test_path_starts_with("/a/b", "/a", true); + test_path_starts_with("a", "a/b", false); + test_path_starts_with("a/b", "a", true); + test_path_starts_with("a/b/c", "a/b", true); + test_path_starts_with("a/b/c", "a/b/c", true); + test_path_starts_with("/aaa/abc", "/aaa/abc", true); + test_path_starts_with("/aaa/abcd", "/aaa/abc", false); + test_path_starts_with("/aaa/abcdef", "/aaa/abc", false); + test_path_starts_with("/aaa/ab", "/aaa/abc", false); + test_path_starts_with("/aaa/abcdef/ccc", "/aaa/abc", false); + test_path_starts_with("/aaa/bbb/ccc", "/aaa/bbb/ccc", true); + test_path_starts_with("/aaa/bbb/ccc", "/aaa/bbb/cccc", false); + test_path_starts_with("/aaa/bbb/ccc", "/aaa/bbb/cc", false); + test_path_starts_with("/aaa/bbb/ccc/", "/aaa/bbb/ccc", true); + test_path_starts_with("/aaa/bbb/ccc/", "/aaa/bbb/cccc", false); + test_path_starts_with("/aaa/bbb/ccc/", "/aaa/bbb/cc", false); + test_path_starts_with("/aaa/bbb/ccc", "/aaa/bbb/ccc/", true); + test_path_starts_with("/aaa/bbb/ccc", "/aaa/bbb/cccc/", false); + test_path_starts_with("/aaa/bbb/ccc", "/aaa/bbb/cc/", false); + test_path_starts_with("/aaa/bbb/ccc/", "/aaa/bbb/ccc/", true); + test_path_starts_with("/aaa/bbb/ccc/", "/aaa/bbb/cccc/", false); + test_path_starts_with("/aaa/bbb/ccc/", "/aaa/bbb/cc/", false); + test_path_starts_with("abc", "abc", true); + test_path_starts_with("abc", "ab", false); + test_path_starts_with("abc", "abcd", false); + test_path_starts_with("abc/", "abc", true); + test_path_starts_with("abc/", "ab", false); + test_path_starts_with("abc/", "abcd", false); + test_path_starts_with("abc", "abc/", true); + test_path_starts_with("abc", "ab/", false); + test_path_starts_with("abc", "abcd/", false); + test_path_starts_with("abc/", "abc/", true); + test_path_starts_with("abc/", "ab/", false); + test_path_starts_with("abc/", "abcd/", false); + test_path_starts_with("abc//", "abc//", true); + test_path_starts_with("abc//", "ab//", false); + test_path_starts_with("abc//", "abcd//", false); + test_path_starts_with("/a/b/c", "", true); + test_path_starts_with("/a/b/c", "/", true); + test_path_starts_with("/a/b/c", "/", true); +} + static void sprintf_new_suite() { char *result; @@ -133,17 +195,17 @@ static void filter_o_opts_test(const char **init, const char **expected) { for (int i = 0; i < argc; ++i) { if (argv[i] == NULL) { printf("Expected %s but got end of argv at index %d with input %s\n", expected[i], i, joined_input); - failures++; + ++failures; break; } if (expected[i] == NULL) { printf("Expected end of args but got %s at index %d with input %s\n", argv[i], i, joined_input); - failures++; + ++failures; break; } if (strcmp(argv[i], expected[i]) != 0) { printf("Expected %s but got %s at index %d with input %s\n", expected[i], argv[i], i, joined_input); - failures++; + ++failures; break; } } @@ -240,6 +302,7 @@ static void filter_o_opts_suite() { static void test_internal_suite() { arena_suite(); my_dirname_suite(); + path_starts_with_suite(); sprintf_new_suite(); filter_o_opts_suite(); } |