mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
127176d72f
PiperOrigin-RevId: 559733768 Change-Id: Ia38f4c176e9f0abbfdb3a8f1109f482d8870eb0f
432 lines
15 KiB
C++
432 lines
15 KiB
C++
// Copyright 2019 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "sandboxed_api/util/fileops.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <cerrno>
|
|
#include <climits>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "sandboxed_api/testing.h"
|
|
#include "sandboxed_api/util/file_helpers.h"
|
|
#include "sandboxed_api/util/status_matchers.h"
|
|
|
|
namespace sapi::file_util {
|
|
|
|
// Forward declare functions that are only used in fileops.cc.
|
|
namespace fileops {
|
|
bool GetCWD(std::string* result);
|
|
bool RemoveLastPathComponent(const std::string& file, std::string* output);
|
|
} // namespace fileops
|
|
|
|
namespace {
|
|
|
|
using ::sapi::IsOk;
|
|
using ::testing::Eq;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::IsFalse;
|
|
using ::testing::IsTrue;
|
|
using ::testing::Ne;
|
|
using ::testing::SizeIs;
|
|
using ::testing::StrEq;
|
|
|
|
class FileOpsTest : public testing::Test {
|
|
protected:
|
|
static void SetUpTestSuite() {
|
|
ASSERT_THAT(chdir(GetTestTempPath().c_str()), Eq(0));
|
|
}
|
|
};
|
|
|
|
TEST_F(FileOpsTest, GetCWDTest) {
|
|
std::string result;
|
|
ASSERT_THAT(fileops::GetCWD(&result), IsTrue());
|
|
EXPECT_THAT(result, StrEq(GetTestTempPath()));
|
|
}
|
|
|
|
TEST_F(FileOpsTest, MakeAbsoluteTest) {
|
|
const auto tmp_dir = GetTestTempPath();
|
|
ASSERT_THAT(chdir(tmp_dir.c_str()), Eq(0));
|
|
EXPECT_THAT(fileops::MakeAbsolute("", ""), StrEq(""));
|
|
EXPECT_THAT(fileops::MakeAbsolute(".", ""), StrEq(tmp_dir));
|
|
EXPECT_THAT(fileops::MakeAbsolute(".", tmp_dir), StrEq(tmp_dir));
|
|
EXPECT_THAT(fileops::MakeAbsolute(".", "/"), StrEq("/"));
|
|
EXPECT_THAT(fileops::MakeAbsolute("/", tmp_dir), StrEq("/"));
|
|
EXPECT_THAT(fileops::MakeAbsolute("/", "/"), StrEq("/"));
|
|
EXPECT_THAT(fileops::MakeAbsolute("/", ""), StrEq("/"));
|
|
EXPECT_THAT(fileops::MakeAbsolute("/foo/bar", ""), StrEq("/foo/bar"));
|
|
EXPECT_THAT(fileops::MakeAbsolute("foo/bar", ""),
|
|
StrEq(absl::StrCat(tmp_dir, "/foo/bar")));
|
|
EXPECT_THAT(fileops::MakeAbsolute("foo/bar", tmp_dir),
|
|
StrEq(absl::StrCat(tmp_dir, "/foo/bar")));
|
|
EXPECT_THAT(fileops::MakeAbsolute("foo/bar", tmp_dir + "/"),
|
|
StrEq(absl::StrCat(tmp_dir, "/foo/bar")));
|
|
}
|
|
|
|
TEST_F(FileOpsTest, ExistsTest) {
|
|
ASSERT_THAT(file::SetContents("exists_test", "", file::Defaults()), IsOk());
|
|
EXPECT_THAT(fileops::Exists("exists_test", false), IsTrue());
|
|
EXPECT_THAT(fileops::Exists("exists_test", true), IsTrue());
|
|
|
|
ASSERT_THAT(symlink("exists_test", "exists_test_link"), Eq(0));
|
|
EXPECT_THAT(fileops::Exists("exists_test_link", false), IsTrue());
|
|
EXPECT_THAT(fileops::Exists("exists_test_link", true), IsTrue());
|
|
|
|
ASSERT_THAT(unlink("exists_test"), Eq(0));
|
|
EXPECT_THAT(fileops::Exists("exists_test_link", false), IsTrue());
|
|
EXPECT_THAT(fileops::Exists("exists_test_link", true), IsFalse());
|
|
|
|
ASSERT_THAT(unlink("exists_test_link"), Eq(0));
|
|
EXPECT_THAT(fileops::Exists("exists_test_link", false), IsFalse());
|
|
EXPECT_THAT(fileops::Exists("exists_test_link", true), IsFalse());
|
|
}
|
|
|
|
TEST_F(FileOpsTest, ReadLinkTest) {
|
|
EXPECT_THAT(fileops::ReadLink("readlink_not_there"), StrEq(""));
|
|
EXPECT_THAT(errno, Eq(ENOENT));
|
|
|
|
ASSERT_THAT(file::SetContents("readlink_file", "", file::Defaults()), IsOk());
|
|
EXPECT_THAT(fileops::ReadLink("readlink_file"), StrEq(""));
|
|
unlink("readlink_file");
|
|
|
|
ASSERT_THAT(symlink("..", "readlink_dotdot"), Eq(0));
|
|
EXPECT_THAT(fileops::ReadLink("readlink_dotdot"), StrEq(".."));
|
|
unlink("readlink_dotdot");
|
|
|
|
ASSERT_THAT(symlink("../", "readlink_dotdotslash"), 0);
|
|
EXPECT_THAT(fileops::ReadLink("readlink_dotdotslash"), "../");
|
|
unlink("readlink_dotdotslash");
|
|
|
|
ASSERT_THAT(symlink("/", "readlink_slash"), 0);
|
|
EXPECT_THAT(fileops::ReadLink("readlink_slash"), "/");
|
|
unlink("readlink_slash");
|
|
|
|
const std::string very_long_name(PATH_MAX - 1, 'f');
|
|
ASSERT_THAT(symlink(very_long_name.c_str(), "readlink_long"), Eq(0));
|
|
EXPECT_THAT(fileops::ReadLink("readlink_long"), StrEq(very_long_name));
|
|
unlink("readlink_long");
|
|
}
|
|
|
|
TEST_F(FileOpsTest, ListDirectoryEntriesFailTest) {
|
|
std::vector<std::string> files;
|
|
std::string error;
|
|
|
|
EXPECT_THAT(fileops::ListDirectoryEntries("new_dir", &files, &error),
|
|
IsFalse());
|
|
EXPECT_THAT(files, IsEmpty());
|
|
EXPECT_THAT(error, StrEq("opendir(new_dir): No such file or directory"));
|
|
}
|
|
|
|
TEST_F(FileOpsTest, ListDirectoryEntriesEmptyTest) {
|
|
std::vector<std::string> files;
|
|
std::string error;
|
|
|
|
ASSERT_THAT(mkdir("new_dir", 0700), Eq(0));
|
|
|
|
EXPECT_THAT(fileops::ListDirectoryEntries("new_dir", &files, &error),
|
|
IsTrue());
|
|
EXPECT_THAT(files, IsEmpty());
|
|
|
|
rmdir("new_dir");
|
|
}
|
|
|
|
TEST_F(FileOpsTest, ListDirectoryEntriesOneFileTest) {
|
|
ASSERT_THAT(mkdir("new_dir", 0700), Eq(0));
|
|
ASSERT_THAT(file::SetContents("new_dir/first", "", file::Defaults()), IsOk());
|
|
|
|
std::vector<std::string> files;
|
|
std::string error;
|
|
EXPECT_THAT(fileops::ListDirectoryEntries("new_dir", &files, &error),
|
|
IsTrue());
|
|
|
|
unlink("new_dir/first");
|
|
rmdir("new_dir");
|
|
|
|
ASSERT_THAT(files, SizeIs(1));
|
|
EXPECT_THAT(files[0], "first");
|
|
}
|
|
|
|
TEST_F(FileOpsTest, ListDirectoryEntriesTest) {
|
|
ASSERT_THAT(mkdir("new_dir", 0700), Eq(0));
|
|
constexpr int kNumFiles = 10;
|
|
for (int i = 0; i < kNumFiles; ++i) {
|
|
ASSERT_THAT(file::SetContents(absl::StrCat("new_dir/file", i), "",
|
|
file::Defaults()),
|
|
IsOk());
|
|
}
|
|
|
|
std::vector<std::string> files;
|
|
std::string error;
|
|
EXPECT_THAT(fileops::ListDirectoryEntries("new_dir", &files, &error),
|
|
IsTrue());
|
|
|
|
fileops::DeleteRecursively("new_dir");
|
|
|
|
ASSERT_THAT(files, SizeIs(kNumFiles));
|
|
std::sort(files.begin(), files.end());
|
|
for (int i = 0; i < kNumFiles; ++i) {
|
|
EXPECT_THAT(files[i], StrEq(absl::StrCat("file", i)));
|
|
}
|
|
}
|
|
|
|
TEST_F(FileOpsTest, RemoveLastPathComponentTest) {
|
|
std::string result;
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/", &result), IsFalse());
|
|
EXPECT_THAT(result, StrEq("/"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("///", &result), IsFalse());
|
|
EXPECT_THAT(result, StrEq("/"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home", &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq("/"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home/", &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq("/"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home///", &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq("/"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home///", &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq("/"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("///home///", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home/someone", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/home"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home///someone", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/home"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home///someone/", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/home"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home///someone//", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/home"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home/someone/file", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/home/someone"));
|
|
|
|
EXPECT_THAT(
|
|
fileops::RemoveLastPathComponent("/home/someone////file", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/home/someone"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home///someone/file", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/home///someone"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/home/someone/file", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("/home/someone"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("no_root", &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq(""));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("no_root/", &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq(""));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("no_root///", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq(""));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("/file", &result), IsTrue());
|
|
EXPECT_THAT(result, "/");
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("no_root/file", &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq("no_root"));
|
|
|
|
result = "no_root";
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq(""));
|
|
|
|
result = "no_root/";
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq(""));
|
|
|
|
result = "no_root///";
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq(""));
|
|
|
|
result = "/file";
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq("/"));
|
|
|
|
result = "no_root/file";
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq("no_root"));
|
|
|
|
EXPECT_THAT(fileops::RemoveLastPathComponent("", &result), IsFalse());
|
|
EXPECT_THAT(result, StrEq(""));
|
|
}
|
|
|
|
TEST_F(FileOpsTest, TestBasename) {
|
|
EXPECT_THAT(fileops::Basename(""), StrEq(""));
|
|
EXPECT_THAT(fileops::Basename("/"), StrEq(""));
|
|
EXPECT_THAT(fileops::Basename("//"), StrEq(""));
|
|
EXPECT_THAT(fileops::Basename("/hello/"), StrEq(""));
|
|
EXPECT_THAT(fileops::Basename("//hello"), StrEq("hello"));
|
|
EXPECT_THAT(fileops::Basename("/hello/world"), StrEq("world"));
|
|
EXPECT_THAT(fileops::Basename("/hello, world"), StrEq("hello, world"));
|
|
}
|
|
|
|
TEST_F(FileOpsTest, TestStripBasename) {
|
|
EXPECT_THAT(fileops::StripBasename(""), StrEq(""));
|
|
EXPECT_THAT(fileops::StripBasename("/"), StrEq("/"));
|
|
EXPECT_THAT(fileops::StripBasename("//"), StrEq("/"));
|
|
EXPECT_THAT(fileops::StripBasename("/hello"), StrEq("/"));
|
|
EXPECT_THAT(fileops::StripBasename("//hello"), StrEq("/"));
|
|
EXPECT_THAT(fileops::StripBasename("/hello/"), StrEq("/hello"));
|
|
EXPECT_THAT(fileops::StripBasename("/hello//"), StrEq("/hello/"));
|
|
EXPECT_THAT(fileops::StripBasename("/hello/world"), StrEq("/hello"));
|
|
EXPECT_THAT(fileops::StripBasename("/hello, world"), StrEq("/"));
|
|
}
|
|
|
|
void SetupDirectory() {
|
|
ASSERT_THAT(mkdir("foo", 0755), Eq(0));
|
|
ASSERT_THAT(mkdir("foo/bar", 0755), Eq(0));
|
|
ASSERT_THAT(mkdir("foo/baz", 0755), Eq(0));
|
|
ASSERT_THAT(file::SetContents("foo/quux", "", file::Defaults()), IsOk());
|
|
ASSERT_THAT(chmod("foo/quux", 0644), Eq(0));
|
|
|
|
ASSERT_THAT(file::SetContents("foo/bar/foo", "", file::Defaults()), IsOk());
|
|
ASSERT_THAT(chmod("foo/bar/foo", 0644), Eq(0));
|
|
ASSERT_THAT(file::SetContents("foo/bar/bar", "", file::Defaults()), IsOk());
|
|
ASSERT_THAT(chmod("foo/bar/bar", 0644), Eq(0));
|
|
|
|
ASSERT_THAT(mkdir("foo/bar/baz", 0755), Eq(0));
|
|
ASSERT_THAT(file::SetContents("foo/bar/baz/foo", "", file::Defaults()),
|
|
IsOk());
|
|
ASSERT_THAT(chmod("foo/bar/baz/foo", 0644), Eq(0));
|
|
}
|
|
|
|
TEST_F(FileOpsTest, CreateDirectoryRecursivelyTest) {
|
|
constexpr char kTestDir[] = "a/b/c";
|
|
EXPECT_THAT(fileops::CreateDirectoryRecursively(kTestDir, 0700), IsTrue());
|
|
EXPECT_THAT(fileops::CreateDirectoryRecursively(kTestDir, 0700), IsTrue());
|
|
}
|
|
|
|
TEST_F(FileOpsTest, DeleteRecursivelyTest) {
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
|
|
EXPECT_THAT(fileops::DeleteRecursively("/not_there"), IsTrue());
|
|
|
|
// Can't stat file
|
|
SetupDirectory();
|
|
ASSERT_THAT(chmod("foo/bar/baz", 0000), Eq(0));
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo/bar/baz/quux"), IsFalse());
|
|
EXPECT_THAT(errno, Eq(EACCES));
|
|
ASSERT_THAT(chmod("foo/bar/baz", 0755), Eq(0));
|
|
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
|
|
struct stat64 st;
|
|
EXPECT_THAT(lstat64("foo", &st), Ne(0));
|
|
|
|
// Can't list subdirectory
|
|
SetupDirectory();
|
|
ASSERT_THAT(chmod("foo/bar/baz", 0000), Eq(0));
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo"), IsFalse());
|
|
EXPECT_THAT(errno, Eq(EACCES));
|
|
ASSERT_THAT(chmod("foo/bar/baz", 0755), Eq(0));
|
|
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
|
|
|
|
// Can't delete file
|
|
SetupDirectory();
|
|
ASSERT_THAT(chmod("foo/bar/baz", 0500), Eq(0));
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo"), IsFalse());
|
|
EXPECT_THAT(errno, Eq(EACCES));
|
|
ASSERT_THAT(chmod("foo/bar/baz", 0755), Eq(0));
|
|
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
|
|
|
|
// Can't delete directory
|
|
SetupDirectory();
|
|
ASSERT_THAT(fileops::DeleteRecursively("foo/bar/baz/foo"), IsTrue());
|
|
ASSERT_THAT(chmod("foo/bar", 0500), Eq(0));
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo/bar/baz"), IsFalse());
|
|
EXPECT_THAT(errno, Eq(EACCES));
|
|
ASSERT_THAT(chmod("foo/bar", 0755), Eq(0));
|
|
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
|
|
}
|
|
|
|
TEST_F(FileOpsTest, ReadLinkAbsoluteTest) {
|
|
const auto tmp_dir = GetTestTempPath();
|
|
ASSERT_THAT(chdir(tmp_dir.c_str()), Eq(0));
|
|
|
|
EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
|
|
ASSERT_THAT(symlink("rel/path", "foo"), Eq(0));
|
|
|
|
const std::string expected_path = absl::StrCat(tmp_dir, "/rel/path");
|
|
const std::string expected_path2 = absl::StrCat(tmp_dir, "/./rel/path");
|
|
std::string result;
|
|
EXPECT_THAT(fileops::ReadLinkAbsolute("foo", &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq(expected_path));
|
|
EXPECT_THAT(fileops::ReadLinkAbsolute("./foo", &result), IsTrue());
|
|
EXPECT_THAT(result, StrEq(expected_path2));
|
|
EXPECT_THAT(fileops::ReadLinkAbsolute(absl::StrCat(tmp_dir, "/foo"), &result),
|
|
IsTrue());
|
|
EXPECT_THAT(result, StrEq(expected_path));
|
|
|
|
result.clear();
|
|
EXPECT_THAT(fileops::ReadLinkAbsolute("/not_there", &result), IsFalse());
|
|
EXPECT_THAT(result, IsEmpty());
|
|
}
|
|
|
|
TEST_F(FileOpsTest, CopyFileTest) {
|
|
const auto tmp_dir = GetTestTempPath();
|
|
// Non-existent source
|
|
EXPECT_THAT(
|
|
fileops::CopyFile("/not/there", absl::StrCat(tmp_dir, "/out"), 0777),
|
|
IsFalse());
|
|
|
|
// Unwritable target
|
|
EXPECT_THAT(fileops::CopyFile("/proc/self/exe", tmp_dir, 0777), IsFalse());
|
|
|
|
EXPECT_THAT(file::SetContents(absl::StrCat(tmp_dir, "/test"), "test\n",
|
|
file::Defaults()),
|
|
IsOk());
|
|
EXPECT_THAT(fileops::CopyFile(absl::StrCat(tmp_dir, "/test"),
|
|
absl::StrCat(tmp_dir, "/test2"), 0666),
|
|
IsTrue());
|
|
|
|
std::string text;
|
|
EXPECT_THAT(file::GetContents(absl::StrCat(tmp_dir, "/test2"), &text,
|
|
file::Defaults()),
|
|
IsOk());
|
|
|
|
EXPECT_THAT(text, StrEq("test\n"));
|
|
|
|
unlink((absl::StrCat(tmp_dir, "/test")).c_str());
|
|
unlink((absl::StrCat(tmp_dir, "/test2")).c_str());
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace sapi::file_util
|