blob: c7c7aa80b7c526cb25c104cbd65d6c61e6b93016 [file] [log] [blame]
// 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))