mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Add unittest for IsEquivalentNode
PiperOrigin-RevId: 433172902 Change-Id: Ie6fb44e682be947fb9f8b856c5e804aa91647a6d
This commit is contained in:
parent
8a5740fbb1
commit
2650834d7c
|
@ -495,6 +495,7 @@ cc_test(
|
||||||
data = ["//sandboxed_api/sandbox2/testcases:minimal_dynamic"],
|
data = ["//sandboxed_api/sandbox2/testcases:minimal_dynamic"],
|
||||||
deps = [
|
deps = [
|
||||||
":mounts",
|
":mounts",
|
||||||
|
":mounttree_cc_proto",
|
||||||
"//sandboxed_api:testing",
|
"//sandboxed_api:testing",
|
||||||
"//sandboxed_api/util:file_base",
|
"//sandboxed_api/util:file_base",
|
||||||
"//sandboxed_api/util:file_helpers",
|
"//sandboxed_api/util:file_helpers",
|
||||||
|
|
|
@ -667,6 +667,7 @@ if(SAPI_ENABLE_TESTS)
|
||||||
absl::strings
|
absl::strings
|
||||||
sapi::file_base
|
sapi::file_base
|
||||||
sandbox2::mounts
|
sandbox2::mounts
|
||||||
|
sandbox2::mounttree_proto
|
||||||
sapi::temp_file
|
sapi::temp_file
|
||||||
sapi::testing
|
sapi::testing
|
||||||
sapi::status_matchers
|
sapi::status_matchers
|
||||||
|
|
|
@ -55,43 +55,6 @@ bool PathContainsNullByte(absl::string_view path) {
|
||||||
return absl::StrContains(path, '\0');
|
return absl::StrContains(path, '\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSameFile(const std::string& path1, const std::string& path2) {
|
|
||||||
struct stat stat1, stat2;
|
|
||||||
if (stat(path1.c_str(), &stat1) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stat(path2.c_str(), &stat2) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsEquivalentNode(const sandbox2::MountTree::Node& n1,
|
|
||||||
const sandbox2::MountTree::Node& n2) {
|
|
||||||
// Node equals 1:1
|
|
||||||
if (google::protobuf::util::MessageDifferencer::Equals(n1, n2)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n1.node_case() != n2.node_case()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether files/dirs are the same (e.g symlinks / hardlinks)
|
|
||||||
switch (n1.node_case()) {
|
|
||||||
case sandbox2::MountTree::Node::kFileNode:
|
|
||||||
return n1.file_node().is_ro() == n2.file_node().is_ro() &&
|
|
||||||
IsSameFile(n1.file_node().outside(), n2.file_node().outside());
|
|
||||||
case sandbox2::MountTree::Node::kDirNode:
|
|
||||||
return n1.dir_node().is_ro() == n2.dir_node().is_ro() &&
|
|
||||||
IsSameFile(n1.dir_node().outside(), n2.dir_node().outside());
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::string_view GetOutsidePath(const MountTree::Node& node) {
|
absl::string_view GetOutsidePath(const MountTree::Node& node) {
|
||||||
switch (node.node_case()) {
|
switch (node.node_case()) {
|
||||||
case MountTree::Node::kFileNode:
|
case MountTree::Node::kFileNode:
|
||||||
|
@ -162,6 +125,47 @@ std::string GetPlatform(absl::string_view interpreter) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
bool IsSameFile(const std::string& path1, const std::string& path2) {
|
||||||
|
struct stat stat1, stat2;
|
||||||
|
if (stat(path1.c_str(), &stat1) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(path2.c_str(), &stat2) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsEquivalentNode(const sandbox2::MountTree::Node& n1,
|
||||||
|
const sandbox2::MountTree::Node& n2) {
|
||||||
|
// Node equals 1:1
|
||||||
|
if (google::protobuf::util::MessageDifferencer::Equals(n1, n2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n1.node_case() != n2.node_case()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether files/dirs are the same (e.g symlinks / hardlinks)
|
||||||
|
switch (n1.node_case()) {
|
||||||
|
case sandbox2::MountTree::Node::kFileNode:
|
||||||
|
return n1.file_node().is_ro() == n2.file_node().is_ro() &&
|
||||||
|
IsSameFile(n1.file_node().outside(), n2.file_node().outside());
|
||||||
|
case sandbox2::MountTree::Node::kDirNode:
|
||||||
|
return n1.dir_node().is_ro() == n2.dir_node().is_ro() &&
|
||||||
|
IsSameFile(n1.dir_node().outside(), n2.dir_node().outside());
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
absl::Status Mounts::Insert(absl::string_view path,
|
absl::Status Mounts::Insert(absl::string_view path,
|
||||||
const MountTree::Node& new_node) {
|
const MountTree::Node& new_node) {
|
||||||
// Some sandboxes allow the inside/outside paths to be partially
|
// Some sandboxes allow the inside/outside paths to be partially
|
||||||
|
@ -225,7 +229,7 @@ absl::Status Mounts::Insert(absl::string_view path,
|
||||||
.first->second);
|
.first->second);
|
||||||
|
|
||||||
if (curtree->has_node()) {
|
if (curtree->has_node()) {
|
||||||
if (IsEquivalentNode(curtree->node(), new_node)) {
|
if (internal::IsEquivalentNode(curtree->node(), new_node)) {
|
||||||
SAPI_RAW_LOG(INFO, "Inserting %s with the same value twice",
|
SAPI_RAW_LOG(INFO, "Inserting %s with the same value twice",
|
||||||
std::string(path).c_str());
|
std::string(path).c_str());
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
|
|
|
@ -26,6 +26,12 @@
|
||||||
|
|
||||||
namespace sandbox2 {
|
namespace sandbox2 {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
bool IsSameFile(const std::string& path1, const std::string& path2);
|
||||||
|
bool IsEquivalentNode(const MountTree::Node& n1, const MountTree::Node& n2);
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
class Mounts {
|
class Mounts {
|
||||||
public:
|
public:
|
||||||
Mounts() {
|
Mounts() {
|
||||||
|
|
|
@ -178,7 +178,7 @@ TEST(MountTreeTest, TestMinimalDynamicBinary) {
|
||||||
|
|
||||||
TEST(MountTreeTest, TestList) {
|
TEST(MountTreeTest, TestList) {
|
||||||
struct TestCase {
|
struct TestCase {
|
||||||
const char *path;
|
const char* path;
|
||||||
const bool is_ro;
|
const bool is_ro;
|
||||||
};
|
};
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -196,7 +196,7 @@ TEST(MountTreeTest, TestList) {
|
||||||
Mounts mounts;
|
Mounts mounts;
|
||||||
|
|
||||||
// Create actual directories and files on disk and selectively add
|
// Create actual directories and files on disk and selectively add
|
||||||
for (const auto &test_case : kTestCases) {
|
for (const auto& test_case : kTestCases) {
|
||||||
const auto inside_path = test_case.path;
|
const auto inside_path = test_case.path;
|
||||||
const std::string outside_path = absl::StrCat("/some/dir/", inside_path);
|
const std::string outside_path = absl::StrCat("/some/dir/", inside_path);
|
||||||
if (absl::EndsWith(outside_path, "/")) {
|
if (absl::EndsWith(outside_path, "/")) {
|
||||||
|
@ -244,6 +244,61 @@ TEST(MountTreeTest, TestList) {
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(MountTreeTest, TestNodeEquivalence) {
|
||||||
|
MountTree::Node nodes[8];
|
||||||
|
MountTree::FileNode* fn0 = nodes[0].mutable_file_node();
|
||||||
|
fn0->set_is_ro(true);
|
||||||
|
fn0->set_outside("foo");
|
||||||
|
MountTree::FileNode* fn1 = nodes[1].mutable_file_node();
|
||||||
|
fn1->set_is_ro(true);
|
||||||
|
fn1->set_outside("bar");
|
||||||
|
MountTree::DirNode* dn0 = nodes[2].mutable_dir_node();
|
||||||
|
dn0->set_is_ro(true);
|
||||||
|
dn0->set_outside("foo");
|
||||||
|
MountTree::DirNode* dn1 = nodes[3].mutable_dir_node();
|
||||||
|
dn1->set_is_ro(true);
|
||||||
|
dn1->set_outside("bar");
|
||||||
|
MountTree::TmpfsNode* tn0 = nodes[4].mutable_tmpfs_node();
|
||||||
|
tn0->set_tmpfs_options("option1");
|
||||||
|
MountTree::TmpfsNode* tn1 = nodes[5].mutable_tmpfs_node();
|
||||||
|
tn1->set_tmpfs_options("option2");
|
||||||
|
MountTree::RootNode* rn0 = nodes[6].mutable_root_node();
|
||||||
|
rn0->set_is_ro(true);
|
||||||
|
MountTree::RootNode* rn1 = nodes[7].mutable_root_node();
|
||||||
|
rn1->set_is_ro(false);
|
||||||
|
|
||||||
|
for (const MountTree::Node n : nodes) {
|
||||||
|
ASSERT_TRUE(n.IsInitialized());
|
||||||
|
}
|
||||||
|
// Compare same file nodes
|
||||||
|
EXPECT_TRUE(internal::IsEquivalentNode(nodes[0], nodes[0]));
|
||||||
|
// Compare with different file node
|
||||||
|
EXPECT_FALSE(internal::IsEquivalentNode(nodes[0], nodes[1]));
|
||||||
|
// compare file node with dir node
|
||||||
|
EXPECT_FALSE(internal::IsEquivalentNode(nodes[0], nodes[2]));
|
||||||
|
|
||||||
|
// Compare same dir nodes
|
||||||
|
EXPECT_TRUE(internal::IsEquivalentNode(nodes[2], nodes[2]));
|
||||||
|
// Compare with different dir node
|
||||||
|
EXPECT_FALSE(internal::IsEquivalentNode(nodes[2], nodes[3]));
|
||||||
|
// Compare dir node with tmpfs node
|
||||||
|
EXPECT_FALSE(internal::IsEquivalentNode(nodes[2], nodes[4]));
|
||||||
|
|
||||||
|
// Compare same tmpfs nodes
|
||||||
|
EXPECT_TRUE(internal::IsEquivalentNode(nodes[4], nodes[4]));
|
||||||
|
// Compare with different tmpfs nodes
|
||||||
|
EXPECT_FALSE(internal::IsEquivalentNode(nodes[4], nodes[5]));
|
||||||
|
// Compare tmpfs node with root node
|
||||||
|
EXPECT_FALSE(internal::IsEquivalentNode(nodes[4], nodes[6]));
|
||||||
|
|
||||||
|
// Compare same root nodes
|
||||||
|
EXPECT_TRUE(internal::IsEquivalentNode(nodes[6], nodes[6]));
|
||||||
|
// Compare different root node
|
||||||
|
EXPECT_FALSE(internal::IsEquivalentNode(nodes[6], nodes[7]));
|
||||||
|
// Compare root node with file node
|
||||||
|
EXPECT_FALSE(internal::IsEquivalentNode(nodes[6], nodes[0]));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(MountsResolvePathTest, Files) {
|
TEST(MountsResolvePathTest, Files) {
|
||||||
Mounts mounts;
|
Mounts mounts;
|
||||||
ASSERT_THAT(mounts.AddFileAt("/A", "/a"), IsOk());
|
ASSERT_THAT(mounts.AddFileAt("/A", "/a"), IsOk());
|
||||||
|
|
Loading…
Reference in New Issue
Block a user