1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <dirent.h>
6 #include <fcntl.h>
7 #include <limits.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13
14 #include <zircon/compiler.h>
15
16 #include "filesystems.h"
17 #include "misc.h"
18
19 // Test cases of '..' where the path can be canonicalized on the client.
test_dot_dot_client(void)20 bool test_dot_dot_client(void) {
21 BEGIN_TEST;
22 ASSERT_EQ(mkdir("::foo", 0755), 0, "");
23 ASSERT_EQ(mkdir("::foo/bit", 0755), 0, "");
24 ASSERT_EQ(mkdir("::foo/bar", 0755), 0, "");
25 ASSERT_EQ(mkdir("::foo/bar/baz", 0755), 0, "");
26
27 expected_dirent_t foo_dir[] = {
28 {false, ".", DT_DIR},
29 {false, "bar", DT_DIR},
30 {false, "bit", DT_DIR},
31 };
32
33 expected_dirent_t bar_dir[] = {
34 {false, ".", DT_DIR},
35 {false, "baz", DT_DIR},
36 };
37
38 // Test cases of client-side dot-dot when moving between directories.
39 DIR* dir = opendir("::foo/bar/..");
40 ASSERT_NONNULL(dir, "");
41 ASSERT_TRUE(fcheck_dir_contents(dir, foo_dir, countof(foo_dir)), "");
42 ASSERT_EQ(closedir(dir), 0, "");
43
44 dir = opendir("::foo/bar/../bit/..//././//");
45 ASSERT_NONNULL(dir, "");
46 ASSERT_TRUE(fcheck_dir_contents(dir, foo_dir, countof(foo_dir)), "");
47 ASSERT_EQ(closedir(dir), 0, "");
48
49 dir = opendir("::foo/bar/baz/../../../foo/bar/baz/..");
50 ASSERT_NONNULL(dir, "");
51 ASSERT_TRUE(fcheck_dir_contents(dir, bar_dir, countof(bar_dir)), "");
52 ASSERT_EQ(closedir(dir), 0, "");
53
54 // Clean up
55 ASSERT_EQ(unlink("::foo/bar/baz"), 0, "");
56 ASSERT_EQ(unlink("::foo/bar"), 0, "");
57 ASSERT_EQ(unlink("::foo/bit"), 0, "");
58 ASSERT_EQ(unlink("::foo"), 0, "");
59 END_TEST;
60 }
61
62 // Test cases of '..' where the path cannot be canonicalized on the client.
test_dot_dot_server(void)63 bool test_dot_dot_server(void) {
64 BEGIN_TEST;
65 ASSERT_EQ(mkdir("::foo", 0755), 0, "");
66 ASSERT_EQ(mkdir("::foo/bar", 0755), 0, "");
67
68 int foo_fd = open("::foo", O_RDONLY | O_DIRECTORY);
69 ASSERT_GT(foo_fd, 0, "");
70
71 // ".." from foo --> Not Supported
72 ASSERT_LT(openat(foo_fd, "..", O_RDONLY | O_DIRECTORY), 0, "");
73
74 // "bar/../.." from foo --> Not supported
75 ASSERT_LT(openat(foo_fd, "bar/../..", O_RDONLY | O_DIRECTORY), 0, "");
76
77 // "../../../../../bar" --> Not supported
78 ASSERT_LT(openat(foo_fd, "../../../../../bar", O_RDONLY | O_DIRECTORY), 0, "");
79
80 // Try to create a file named '..'
81 ASSERT_LT(openat(foo_fd, "..", O_RDWR | O_CREAT), 0, "");
82 ASSERT_LT(openat(foo_fd, ".", O_RDWR | O_CREAT), 0, "");
83
84 // Try to create a directory named '..'
85 ASSERT_LT(mkdirat(foo_fd, "..", 0666), 0, "");
86 ASSERT_LT(mkdirat(foo_fd, ".", 0666), 0, "");
87
88 // Clean up
89 ASSERT_EQ(close(foo_fd), 0, "");
90 ASSERT_EQ(unlink("::foo/bar"), 0, "");
91 ASSERT_EQ(unlink("::foo"), 0, "");
92 END_TEST;
93 }
94
95 // Test cases of '..' which operate on multiple paths.
96 // This is mostly intended to test other pathways for client-side
97 // cleaning operations.
test_dot_dot_rename(void)98 bool test_dot_dot_rename(void) {
99 BEGIN_TEST;
100 ASSERT_EQ(mkdir("::foo", 0755), 0, "");
101 ASSERT_EQ(mkdir("::foo/bit", 0755), 0, "");
102 ASSERT_EQ(mkdir("::foo/bar", 0755), 0, "");
103 ASSERT_EQ(mkdir("::foo/bar/baz", 0755), 0, "");
104
105 expected_dirent_t foo_dir_bit[] = {
106 {false, ".", DT_DIR},
107 {false, "bar", DT_DIR},
108 {false, "bit", DT_DIR},
109 };
110
111 expected_dirent_t foo_dir_bits[] = {
112 {false, ".", DT_DIR},
113 {false, "bar", DT_DIR},
114 {false, "bits", DT_DIR},
115 };
116
117 // Check that the source is cleaned
118 ASSERT_EQ(rename("::foo/bar/./../bit/./../bit", "::foo/bits"), 0, "");
119 ASSERT_TRUE(check_dir_contents("::foo", foo_dir_bits, countof(foo_dir_bits)), "");
120
121 // Check that the destination is cleaned
122 ASSERT_EQ(rename("::foo/bits", "::foo/bar/baz/../../././bit"), 0, "");
123 ASSERT_TRUE(check_dir_contents("::foo", foo_dir_bit, countof(foo_dir_bit)), "");
124
125 // Check that both are cleaned
126 ASSERT_EQ(rename("::foo/bar/../bit/.", "::foo/bar/baz/../../././bits"), 0, "");
127 ASSERT_TRUE(check_dir_contents("::foo", foo_dir_bits, countof(foo_dir_bits)), "");
128
129 // Check that both are cleaned (including trailing '/')
130 ASSERT_EQ(rename("::foo/./bar/../bits/", "::foo/bar/baz/../../././bit/.//"), 0, "");
131 ASSERT_TRUE(check_dir_contents("::foo", foo_dir_bit, countof(foo_dir_bit)), "");
132
133 // Clean up
134 ASSERT_EQ(unlink("::foo/bar/baz"), 0, "");
135 ASSERT_EQ(unlink("::foo/bar"), 0, "");
136 ASSERT_EQ(unlink("::foo/bit"), 0, "");
137 ASSERT_EQ(unlink("::foo"), 0, "");
138 END_TEST;
139 }
140
141 RUN_FOR_ALL_FILESYSTEMS(dot_dot_tests,
142 RUN_TEST_MEDIUM(test_dot_dot_client)
143 RUN_TEST_MEDIUM(test_dot_dot_server)
144 RUN_TEST_MEDIUM(test_dot_dot_rename)
145 )
146