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"],
|
||||
deps = [
|
||||
":mounts",
|
||||
":mounttree_cc_proto",
|
||||
"//sandboxed_api:testing",
|
||||
"//sandboxed_api/util:file_base",
|
||||
"//sandboxed_api/util:file_helpers",
|
||||
|
|
|
@ -667,6 +667,7 @@ if(SAPI_ENABLE_TESTS)
|
|||
absl::strings
|
||||
sapi::file_base
|
||||
sandbox2::mounts
|
||||
sandbox2::mounttree_proto
|
||||
sapi::temp_file
|
||||
sapi::testing
|
||||
sapi::status_matchers
|
||||
|
|
|
@ -55,43 +55,6 @@ bool PathContainsNullByte(absl::string_view path) {
|
|||
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) {
|
||||
switch (node.node_case()) {
|
||||
case MountTree::Node::kFileNode:
|
||||
|
@ -162,6 +125,47 @@ std::string GetPlatform(absl::string_view interpreter) {
|
|||
|
||||
} // 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,
|
||||
const MountTree::Node& new_node) {
|
||||
// Some sandboxes allow the inside/outside paths to be partially
|
||||
|
@ -225,7 +229,7 @@ absl::Status Mounts::Insert(absl::string_view path,
|
|||
.first->second);
|
||||
|
||||
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",
|
||||
std::string(path).c_str());
|
||||
return absl::OkStatus();
|
||||
|
|
|
@ -26,6 +26,12 @@
|
|||
|
||||
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 {
|
||||
public:
|
||||
Mounts() {
|
||||
|
|
|
@ -178,7 +178,7 @@ TEST(MountTreeTest, TestMinimalDynamicBinary) {
|
|||
|
||||
TEST(MountTreeTest, TestList) {
|
||||
struct TestCase {
|
||||
const char *path;
|
||||
const char* path;
|
||||
const bool is_ro;
|
||||
};
|
||||
// clang-format off
|
||||
|
@ -196,7 +196,7 @@ TEST(MountTreeTest, TestList) {
|
|||
Mounts mounts;
|
||||
|
||||
// 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 std::string outside_path = absl::StrCat("/some/dir/", inside_path);
|
||||
if (absl::EndsWith(outside_path, "/")) {
|
||||
|
@ -244,6 +244,61 @@ TEST(MountTreeTest, TestList) {
|
|||
// 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) {
|
||||
Mounts mounts;
|
||||
ASSERT_THAT(mounts.AddFileAt("/A", "/a"), IsOk());
|
||||
|
|
Loading…
Reference in New Issue
Block a user