aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Pärtel <martin.partel@gmail.com>2022-10-17 08:51:29 +0300
committerMartin Pärtel <martin.partel@gmail.com>2022-10-17 08:51:29 +0300
commit6afff1fc626904b3036822d571fe0051094d3449 (patch)
tree095379cad8cab2d5da6fe3fe1d1ff5a43a292119
parent6fa5cd6480c83240cff72265ccdc45bcdb9576f6 (diff)
downloadbindfs-6afff1fc626904b3036822d571fe0051094d3449.tar.gz
Fixed --resolve-symlinks when mountpoint name starts with source dir name.
Fixes #122
-rw-r--r--ChangeLog5
-rw-r--r--src/bindfs.c4
-rw-r--r--src/misc.c39
-rw-r--r--src/misc.h4
-rw-r--r--tests/internals/test_common.h2
-rw-r--r--tests/internals/test_internals.c73
6 files changed, 119 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index 2076ab1..deeb314 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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;
diff --git a/src/misc.c b/src/misc.c
index cedd247..122497d 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -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*));
diff --git a/src/misc.h b/src/misc.h
index 360ddd1..07c0472 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -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();
}