summaryrefslogtreecommitdiff
path: root/src/user/app/tests/kernel/path.c
blob: ddaf6928a3dedcc95cab1970b5551a24545849ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include "../tests.h"
#include <camellia/path.h>
#include <string.h>
#include <user/lib/compat.h>
#include <user/lib/fs/misc.h>

static void test_path_simplify(void) {
	const char *testcases[][2] = {
		{"/",         "/"},
		{"/.",        "/"},
		{"//",        "/"},
		{"/asdf",     "/asdf"},
		{"/asdf/",    "/asdf/"},
		{"/asdf//",   "/asdf/"},
		{"/asdf/./",  "/asdf/"},
		{"/a/./b",    "/a/b"},
		{"/a/./b/",   "/a/b/"},

		// some slightly less easy cases
		{"/asdf/..",  "/"},
		{"/asdf/../", "/"},
		{"/asdf/.",   "/asdf/"},
		{"/asdf//.",  "/asdf/"},

		{"/foo/bar/..", "/foo/"},
		{"/foo/bar/../baz",  "/foo/baz"},
		{"/foo/bar/../baz/", "/foo/baz/"},
		{"/foo/bar/xyz/..",  "/foo/bar/"},
		{"/foo/bar/xyz/../", "/foo/bar/"},

		// going under the root or close to it
		{"/..",        NULL},
		{"/../asdf",   NULL},
		{"/../asdf/",  NULL},
		{"/./a/../..", NULL},
		{"/a/a/../..", "/"},
		{"/a/../a/..", "/"},
		{"/a/../../a", NULL},
		{"/../a/../a", NULL},
		{"/../../a/a", NULL},
		{"/////../..", NULL},
		{"//a//../..", NULL},

		// relative paths aren't allowed
		{"relative",   NULL},
		{"some/stuff", NULL},
		{"./stuff",    NULL},
		{"../stuff",   NULL},
		{"",           NULL},
	};

	char buf[256];
	for (size_t i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) {
		const char *input    = testcases[i][0];
		const char *expected = testcases[i][1];
		int len = path_simplify(input, buf, strlen(input));
		if (expected == NULL) {
			test(len == 0);
		} else {
			// TODO an argument for printing info on test failure
			test(len == (int)strlen(expected) && !memcmp(expected, buf, len));
		}
	}
}

static void mount_resolve_drv(const char *path) {
	if (fork2_n_mount(path)) return;

	struct fs_wait_response res;
	while (!c0_fs_wait(NULL, 0, &res)) {
		// TODO does the first argument of c0_fs_respond need to be non-const?
		c0_fs_respond((void*)path, strlen(path), 0);
	}
	exit(1);
}

static void test_mount_resolve(void) {
	const char *testcases[][2] = {
		{"/",              "/"},
		{"/test",          "/"},
		{"/dir",           "/dir"},
		{"/dir/..",        "/"},
		{"/dir/../dir",    "/dir"},
		{"/dirry",         "/"},
		{"/dir/",          "/dir"},
		{"/dir/shadowed",  "/dir"},
		{"/dir/shadowed/", "/dir"},
	};
	mount_resolve_drv("/");
	mount_resolve_drv("/dir/shadowed");
	mount_resolve_drv("/dir");

	char buf[16];
	for (size_t i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) {
		const char *input    = testcases[i][0];
		const char *expected = testcases[i][1];
		handle_t h = _syscall_open(input, strlen(input), 0);
		test(h >= 0);
		int len = _syscall_read(h, buf, sizeof buf, 0);
		test(len == (int)strlen(expected) && !memcmp(expected, buf, len));
		_syscall_close(h);
	}
}

void r_k_path(void) {
	run_test(test_path_simplify);
	run_test(test_mount_resolve);
}