// Copyright 2019 The Chromium OS 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 "brillo/files/file_util_test.h" #include #include #include #include #include #include namespace brillo { #define TO_STRING_HELPER(x) \ case brillo::SafeFD::Error::x: \ return #x; std::string to_string(brillo::SafeFD::Error err) { switch (err) { TO_STRING_HELPER(kNoError) TO_STRING_HELPER(kBadArgument) TO_STRING_HELPER(kNotInitialized) TO_STRING_HELPER(kIOError) TO_STRING_HELPER(kDoesNotExist) TO_STRING_HELPER(kSymlinkDetected) TO_STRING_HELPER(kWrongType) TO_STRING_HELPER(kWrongUID) TO_STRING_HELPER(kWrongGID) TO_STRING_HELPER(kWrongPermissions) TO_STRING_HELPER(kExceededMaximum) default: return std::string("unknown (") + std::to_string(static_cast(err)) + ")"; } } #undef TO_STRING_HELPER std::ostream& operator<<(std::ostream& os, const brillo::SafeFD::Error err) { return os << to_string(err); // whatever needed to print bar to os } std::string GetRandomSuffix() { const int kBufferSize = 6; unsigned char buffer[kBufferSize]; base::RandBytes(buffer, base::size(buffer)); return base::HexEncode(buffer, base::size(buffer)); } void FileTest::SetUpTestCase() { umask(0); } FileTest::FileTest() { CHECK(temp_dir_.CreateUniqueTempDir()) << strerror(errno); sub_dir_path_ = temp_dir_.GetPath().Append(kSubdirName); file_path_ = sub_dir_path_.Append(kFileName); std::string path = temp_dir_.GetPath().value(); temp_dir_path_.reserve(path.size() + 1); temp_dir_path_.assign(temp_dir_.GetPath().value().begin(), temp_dir_.GetPath().value().end()); temp_dir_path_.push_back('\0'); CHECK_EQ(chmod(temp_dir_path_.data(), SafeFD::kDefaultDirPermissions), 0); SafeFD::SetRootPathForTesting(temp_dir_path_.data()); root_ = SafeFD::Root().first; CHECK(root_.is_valid()); } bool FileTest::SetupSubdir() { if (!base::CreateDirectory(sub_dir_path_)) { PLOG(ERROR) << "Failed to create '" << sub_dir_path_.value() << "'"; return false; } if (chmod(sub_dir_path_.value().c_str(), SafeFD::kDefaultDirPermissions) != 0) { PLOG(ERROR) << "Failed to set permissions of '" << sub_dir_path_.value() << "'"; return false; } return true; } bool FileTest::SetupSymlinks() { symlink_file_path_ = temp_dir_.GetPath().Append(kSymbolicFileName); symlink_dir_path_ = temp_dir_.GetPath().Append(kSymbolicDirName); if (!base::CreateSymbolicLink(file_path_, symlink_file_path_)) { PLOG(ERROR) << "Failed to create symlink to '" << symlink_file_path_.value() << "'"; return false; } if (!base::CreateSymbolicLink(temp_dir_.GetPath(), symlink_dir_path_)) { PLOG(ERROR) << "Failed to create symlink to'" << symlink_dir_path_.value() << "'"; return false; } return true; } bool FileTest::WriteFile(const std::string& contents) { if (!SetupSubdir()) { return false; } if (contents.length() != base::WriteFile(file_path_, contents.c_str(), contents.length())) { PLOG(ERROR) << "base::WriteFile failed"; return false; } if (chmod(file_path_.value().c_str(), SafeFD::kDefaultFilePermissions) != 0) { PLOG(ERROR) << "chmod failed"; return false; } return true; } void FileTest::ExpectFileContains(const std::string& contents) { EXPECT_TRUE(base::PathExists(file_path_)); std::string new_contents; EXPECT_TRUE(base::ReadFileToString(file_path_, &new_contents)); EXPECT_EQ(contents, new_contents); } void FileTest::ExpectPermissions(base::FilePath path, int permissions) { int actual_permissions = 0; // This breaks out of the ExpectPermissions() call but not the test case. ASSERT_TRUE(base::GetPosixFilePermissions(path, &actual_permissions)); EXPECT_EQ(permissions, actual_permissions); } // Creates a file with a random name in the temporary directory. base::FilePath FileTest::GetTempName() { return temp_dir_.GetPath().Append(GetRandomSuffix()); } constexpr char FileTest::kFileName[]; constexpr char FileTest::kSubdirName[]; constexpr char FileTest::kSymbolicFileName[]; constexpr char FileTest::kSymbolicDirName[]; class FileUtilTest : public FileTest {}; TEST_F(FileUtilTest, GetFDPath_SimpleSuccess) { EXPECT_EQ(GetFDPath(root_.get()), temp_dir_.GetPath()); } TEST_F(FileUtilTest, GetFDPath_BadFD) { base::FilePath path = GetFDPath(-1); EXPECT_TRUE(path.empty()); } TEST_F(FileUtilTest, OpenOrRemakeDir_SimpleSuccess) { SafeFD::Error err; SafeFD dir; std::tie(dir, err) = root_.OpenExistingDir(temp_dir_.GetPath()); EXPECT_EQ(err, SafeFD::Error::kNoError); ASSERT_TRUE(dir.is_valid()); SafeFD subdir; std::tie(subdir, err) = OpenOrRemakeDir(&dir, kSubdirName); EXPECT_EQ(err, SafeFD::Error::kNoError); EXPECT_TRUE(subdir.is_valid()); } TEST_F(FileUtilTest, OpenOrRemakeDir_SuccessAfterRetry) { ASSERT_NE(base::WriteFile(sub_dir_path_, "", 0), -1); SafeFD::Error err; SafeFD dir; std::tie(dir, err) = root_.OpenExistingDir(temp_dir_.GetPath()); EXPECT_EQ(err, SafeFD::Error::kNoError); ASSERT_TRUE(dir.is_valid()); SafeFD subdir; std::tie(subdir, err) = OpenOrRemakeDir(&dir, kSubdirName); EXPECT_EQ(err, SafeFD::Error::kNoError); EXPECT_TRUE(subdir.is_valid()); } TEST_F(FileUtilTest, OpenOrRemakeDir_BadArgument) { SafeFD::Error err; SafeFD dir; std::tie(dir, err) = root_.OpenExistingDir(temp_dir_.GetPath()); EXPECT_EQ(err, SafeFD::Error::kNoError); ASSERT_TRUE(dir.is_valid()); SafeFD subdir; std::tie(subdir, err) = OpenOrRemakeDir(&dir, "."); EXPECT_EQ(err, SafeFD::Error::kBadArgument); EXPECT_FALSE(subdir.is_valid()); std::tie(subdir, err) = OpenOrRemakeDir(&dir, ".."); EXPECT_EQ(err, SafeFD::Error::kBadArgument); EXPECT_FALSE(subdir.is_valid()); std::tie(subdir, err) = OpenOrRemakeDir(&dir, "a/a"); EXPECT_EQ(err, SafeFD::Error::kBadArgument); EXPECT_FALSE(subdir.is_valid()); } TEST_F(FileUtilTest, OpenOrRemakeDir_NotInitialized) { SafeFD::Error err; SafeFD dir; SafeFD subdir; std::tie(subdir, err) = OpenOrRemakeDir(&dir, kSubdirName); EXPECT_EQ(err, SafeFD::Error::kNotInitialized); EXPECT_FALSE(subdir.is_valid()); } TEST_F(FileUtilTest, OpenOrRemakeDir_IOError) { SafeFD::Error err; SafeFD dir; std::tie(dir, err) = root_.OpenExistingDir(temp_dir_.GetPath()); EXPECT_EQ(err, SafeFD::Error::kNoError); ASSERT_TRUE(dir.is_valid()); ASSERT_EQ(chmod(temp_dir_path_.data(), 0000), 0); SafeFD subdir; std::tie(subdir, err) = OpenOrRemakeDir(&dir, kSubdirName); EXPECT_EQ(err, SafeFD::Error::kIOError); EXPECT_FALSE(subdir.is_valid()); } TEST_F(FileUtilTest, OpenOrRemakeFile_SimpleSuccess) { ASSERT_TRUE(SetupSubdir()); SafeFD::Error err; SafeFD dir; std::tie(dir, err) = root_.OpenExistingDir(sub_dir_path_); EXPECT_EQ(err, SafeFD::Error::kNoError); ASSERT_TRUE(dir.is_valid()); SafeFD file; std::tie(file, err) = OpenOrRemakeFile(&dir, kFileName); EXPECT_EQ(err, SafeFD::Error::kNoError); EXPECT_TRUE(file.is_valid()); } TEST_F(FileUtilTest, OpenOrRemakeFile_SuccessAfterRetry) { ASSERT_TRUE(SetupSubdir()); ASSERT_TRUE(base::CreateDirectory(file_path_)); SafeFD::Error err; SafeFD dir; std::tie(dir, err) = root_.OpenExistingDir(sub_dir_path_); EXPECT_EQ(err, SafeFD::Error::kNoError); ASSERT_TRUE(dir.is_valid()); SafeFD file; std::tie(file, err) = OpenOrRemakeFile(&dir, kFileName); EXPECT_EQ(err, SafeFD::Error::kNoError); EXPECT_TRUE(file.is_valid()); } TEST_F(FileUtilTest, OpenOrRemakeFile_NotInitialized) { ASSERT_TRUE(SetupSubdir()); SafeFD::Error err; SafeFD dir; SafeFD file; std::tie(file, err) = OpenOrRemakeFile(&dir, kFileName); EXPECT_EQ(err, SafeFD::Error::kNotInitialized); EXPECT_FALSE(file.is_valid()); } TEST_F(FileUtilTest, OpenOrRemakeFile_IOError) { ASSERT_TRUE(SetupSubdir()); SafeFD::Error err; SafeFD dir; std::tie(dir, err) = root_.OpenExistingDir(sub_dir_path_); EXPECT_EQ(err, SafeFD::Error::kNoError); ASSERT_TRUE(dir.is_valid()); ASSERT_EQ(chmod(sub_dir_path_.value().c_str(), 0000), 0); SafeFD file; std::tie(file, err) = OpenOrRemakeFile(&dir, kFileName); EXPECT_EQ(err, SafeFD::Error::kIOError); EXPECT_FALSE(file.is_valid()); } } // namespace brillo