| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <zircon/compiler.h> |
| |
| #include "filesystems.h" |
| #include "misc.h" |
| |
| // Test cases of '..' where the path can be canonicalized on the client. |
| bool test_dot_dot_client(void) { |
| BEGIN_TEST; |
| ASSERT_EQ(mkdir("::foo", 0755), 0, ""); |
| ASSERT_EQ(mkdir("::foo/bit", 0755), 0, ""); |
| ASSERT_EQ(mkdir("::foo/bar", 0755), 0, ""); |
| ASSERT_EQ(mkdir("::foo/bar/baz", 0755), 0, ""); |
| |
| expected_dirent_t foo_dir[] = { |
| {false, ".", DT_DIR}, |
| {false, "bar", DT_DIR}, |
| {false, "bit", DT_DIR}, |
| }; |
| |
| expected_dirent_t bar_dir[] = { |
| {false, ".", DT_DIR}, |
| {false, "baz", DT_DIR}, |
| }; |
| |
| // Test cases of client-side dot-dot when moving between directories. |
| DIR* dir = opendir("::foo/bar/.."); |
| ASSERT_NONNULL(dir, ""); |
| ASSERT_TRUE(fcheck_dir_contents(dir, foo_dir, countof(foo_dir)), ""); |
| ASSERT_EQ(closedir(dir), 0, ""); |
| |
| dir = opendir("::foo/bar/../bit/..//././//"); |
| ASSERT_NONNULL(dir, ""); |
| ASSERT_TRUE(fcheck_dir_contents(dir, foo_dir, countof(foo_dir)), ""); |
| ASSERT_EQ(closedir(dir), 0, ""); |
| |
| dir = opendir("::foo/bar/baz/../../../foo/bar/baz/.."); |
| ASSERT_NONNULL(dir, ""); |
| ASSERT_TRUE(fcheck_dir_contents(dir, bar_dir, countof(bar_dir)), ""); |
| ASSERT_EQ(closedir(dir), 0, ""); |
| |
| // Clean up |
| ASSERT_EQ(unlink("::foo/bar/baz"), 0, ""); |
| ASSERT_EQ(unlink("::foo/bar"), 0, ""); |
| ASSERT_EQ(unlink("::foo/bit"), 0, ""); |
| ASSERT_EQ(unlink("::foo"), 0, ""); |
| END_TEST; |
| } |
| |
| // Test cases of '..' where the path cannot be canonicalized on the client. |
| bool test_dot_dot_server(void) { |
| BEGIN_TEST; |
| ASSERT_EQ(mkdir("::foo", 0755), 0, ""); |
| ASSERT_EQ(mkdir("::foo/bar", 0755), 0, ""); |
| |
| int foo_fd = open("::foo", O_RDONLY | O_DIRECTORY); |
| ASSERT_GT(foo_fd, 0, ""); |
| |
| // ".." from foo --> Not Supported |
| ASSERT_LT(openat(foo_fd, "..", O_RDONLY | O_DIRECTORY), 0, ""); |
| |
| // "bar/../.." from foo --> Not supported |
| ASSERT_LT(openat(foo_fd, "bar/../..", O_RDONLY | O_DIRECTORY), 0, ""); |
| |
| // "../../../../../bar" --> Not supported |
| ASSERT_LT(openat(foo_fd, "../../../../../bar", O_RDONLY | O_DIRECTORY), 0, ""); |
| |
| // Try to create a file named '..' |
| ASSERT_LT(openat(foo_fd, "..", O_RDWR | O_CREAT), 0, ""); |
| ASSERT_LT(openat(foo_fd, ".", O_RDWR | O_CREAT), 0, ""); |
| |
| // Try to create a directory named '..' |
| ASSERT_LT(mkdirat(foo_fd, "..", 0666), 0, ""); |
| ASSERT_LT(mkdirat(foo_fd, ".", 0666), 0, ""); |
| |
| // Clean up |
| ASSERT_EQ(close(foo_fd), 0, ""); |
| ASSERT_EQ(unlink("::foo/bar"), 0, ""); |
| ASSERT_EQ(unlink("::foo"), 0, ""); |
| END_TEST; |
| } |
| |
| // Test cases of '..' which operate on multiple paths. |
| // This is mostly intended to test other pathways for client-side |
| // cleaning operations. |
| bool test_dot_dot_rename(void) { |
| BEGIN_TEST; |
| ASSERT_EQ(mkdir("::foo", 0755), 0, ""); |
| ASSERT_EQ(mkdir("::foo/bit", 0755), 0, ""); |
| ASSERT_EQ(mkdir("::foo/bar", 0755), 0, ""); |
| ASSERT_EQ(mkdir("::foo/bar/baz", 0755), 0, ""); |
| |
| expected_dirent_t foo_dir_bit[] = { |
| {false, ".", DT_DIR}, |
| {false, "bar", DT_DIR}, |
| {false, "bit", DT_DIR}, |
| }; |
| |
| expected_dirent_t foo_dir_bits[] = { |
| {false, ".", DT_DIR}, |
| {false, "bar", DT_DIR}, |
| {false, "bits", DT_DIR}, |
| }; |
| |
| // Check that the source is cleaned |
| ASSERT_EQ(rename("::foo/bar/./../bit/./../bit", "::foo/bits"), 0, ""); |
| ASSERT_TRUE(check_dir_contents("::foo", foo_dir_bits, countof(foo_dir_bits)), ""); |
| |
| // Check that the destination is cleaned |
| ASSERT_EQ(rename("::foo/bits", "::foo/bar/baz/../../././bit"), 0, ""); |
| ASSERT_TRUE(check_dir_contents("::foo", foo_dir_bit, countof(foo_dir_bit)), ""); |
| |
| // Check that both are cleaned |
| ASSERT_EQ(rename("::foo/bar/../bit/.", "::foo/bar/baz/../../././bits"), 0, ""); |
| ASSERT_TRUE(check_dir_contents("::foo", foo_dir_bits, countof(foo_dir_bits)), ""); |
| |
| // Check that both are cleaned (including trailing '/') |
| ASSERT_EQ(rename("::foo/./bar/../bits/", "::foo/bar/baz/../../././bit/.//"), 0, ""); |
| ASSERT_TRUE(check_dir_contents("::foo", foo_dir_bit, countof(foo_dir_bit)), ""); |
| |
| // Clean up |
| ASSERT_EQ(unlink("::foo/bar/baz"), 0, ""); |
| ASSERT_EQ(unlink("::foo/bar"), 0, ""); |
| ASSERT_EQ(unlink("::foo/bit"), 0, ""); |
| ASSERT_EQ(unlink("::foo"), 0, ""); |
| END_TEST; |
| } |
| |
| RUN_FOR_ALL_FILESYSTEMS(dot_dot_tests, |
| RUN_TEST_MEDIUM(test_dot_dot_client) RUN_TEST_MEDIUM(test_dot_dot_server) |
| RUN_TEST_MEDIUM(test_dot_dot_rename)) |