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