/* * Copyright (C) 2021 The Android Open Source Project * * 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 * * http://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 #include #include #include #include #include #include #include "restorable_file.h" #include "unique_file.h" #include "utils.h" #undef LOG_TAG #define LOG_TAG "installd_file_test" namespace { constexpr char kFileTestDir[] = "/data/local/tmp/installd_file_test_data"; constexpr char kTmpFileSuffix[] = ".tmp"; constexpr char kBackupFileSuffix[] = ".backup"; void UnlinkWithAssert(const std::string& path) { ASSERT_EQ(0, unlink(path.c_str())); } } // namespace namespace android { namespace installd { // Add these as macros as functions make it hard to tell where the failure has happened. #define ASSERT_FILE_NOT_EXISTING(path) \ { \ struct stat st; \ ASSERT_NE(0, ::stat(path.c_str(), &st)); \ } #define ASSERT_FILE_EXISTING(path) \ { \ struct stat st; \ ASSERT_EQ(0, ::stat(path.c_str(), &st)); \ } #define ASSERT_FILE_CONTENT(path, expectedContent) ASSERT_EQ(expectedContent, ReadTestFile(path)) #define ASSERT_FILE_OPEN(path, fd) \ { \ fd = open(path.c_str(), O_RDWR); \ ASSERT_TRUE(fd >= 0); \ } #define ASSERT_WRITE_TO_FD(fd, content) \ ASSERT_TRUE(android::base::WriteStringToFd(content, android::base::borrowed_fd(fd))) class FileTest : public testing::Test { protected: virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(nullptr); ASSERT_EQ(0, create_dir_if_needed(kFileTestDir, 0777)); } virtual void TearDown() { system(android::base::StringPrintf("rm -rf %s", kFileTestDir).c_str()); } std::string GetTestFilePath(const std::string& fileName) { return android::base::StringPrintf("%s/%s", kFileTestDir, fileName.c_str()); } void CreateTestFileWithContents(const std::string& path, const std::string& content) { ALOGI("CreateTestFileWithContents:%s", path.c_str()); ASSERT_TRUE(android::base::WriteStringToFile(content, path)); } std::string GetTestName() { std::string name(testing::UnitTest::GetInstance()->current_test_info()->name()); return name; } std::string ReadTestFile(const std::string& path) { std::string content; bool r = android::base::ReadFileToString(path, &content); if (!r) { PLOG(ERROR) << "Cannot read file:" << path; } return content; } }; TEST_F(FileTest, TestUniqueFileMoveConstruction) { const int fd = 101; std::string testFile = GetTestFilePath(GetTestName()); UniqueFile uf1(fd, testFile); uf1.DisableAutoClose(); UniqueFile uf2(std::move(uf1)); ASSERT_EQ(fd, uf2.fd()); ASSERT_EQ(testFile, uf2.path()); } TEST_F(FileTest, TestUniqueFileAssignment) { const int fd1 = 101; const int fd2 = 102; std::string testFile1 = GetTestFilePath(GetTestName()); std::string testFile2 = GetTestFilePath(GetTestName() + "2"); UniqueFile uf1(fd1, testFile1); uf1.DisableAutoClose(); UniqueFile uf2(fd2, testFile2); uf2.DisableAutoClose(); ASSERT_EQ(fd2, uf2.fd()); ASSERT_EQ(testFile2, uf2.path()); uf2 = std::move(uf1); ASSERT_EQ(fd1, uf2.fd()); ASSERT_EQ(testFile1, uf2.path()); } TEST_F(FileTest, TestUniqueFileCleanup) { std::string testFile = GetTestFilePath(GetTestName()); CreateTestFileWithContents(testFile, "OriginalContent"); int fd; ASSERT_FILE_OPEN(testFile, fd); { UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); } ASSERT_FILE_NOT_EXISTING(testFile); } TEST_F(FileTest, TestUniqueFileNoCleanup) { std::string testFile = GetTestFilePath(GetTestName()); CreateTestFileWithContents(testFile, "OriginalContent"); int fd; ASSERT_FILE_OPEN(testFile, fd); { UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); uf.DisableCleanup(); } ASSERT_FILE_CONTENT(testFile, "OriginalContent"); } TEST_F(FileTest, TestUniqueFileFd) { std::string testFile = GetTestFilePath(GetTestName()); CreateTestFileWithContents(testFile, "OriginalContent"); int fd; ASSERT_FILE_OPEN(testFile, fd); UniqueFile uf(fd, testFile, UnlinkWithAssert); ASSERT_EQ(fd, uf.fd()); uf.reset(); ASSERT_EQ(-1, uf.fd()); } TEST_F(FileTest, TestRestorableFileMoveConstruction) { std::string testFile = GetTestFilePath(GetTestName()); RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile, 0600); int fd = rf1.fd(); RestorableFile rf2(std::move(rf1)); ASSERT_EQ(fd, rf2.fd()); ASSERT_EQ(testFile, rf2.path()); } TEST_F(FileTest, TestRestorableFileAssignment) { std::string testFile1 = GetTestFilePath(GetTestName()); std::string testFile2 = GetTestFilePath(GetTestName() + "2"); RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile1, 0600); int fd1 = rf1.fd(); RestorableFile rf2 = RestorableFile::CreateWritableFile(testFile2, 0600); int fd2 = rf2.fd(); ASSERT_EQ(fd2, rf2.fd()); ASSERT_EQ(testFile2, rf2.path()); rf2 = std::move(rf1); ASSERT_EQ(fd1, rf2.fd()); ASSERT_EQ(testFile1, rf2.path()); } TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithReset) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_FILE_EXISTING(tmpFile); const UniqueFile& uf = rf.GetUniqueFile(); ASSERT_EQ(rf.fd(), uf.fd()); ASSERT_EQ(rf.path(), uf.path()); rf.reset(); ASSERT_EQ(rf.fd(), uf.fd()); ASSERT_EQ(rf.path(), uf.path()); ASSERT_EQ(-1, rf.fd()); ASSERT_TRUE(rf.path().empty()); } } TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithCommit) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; std::string backupFile = testFile + kBackupFileSuffix; { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_FILE_EXISTING(tmpFile); const UniqueFile& uf = rf.GetUniqueFile(); ASSERT_EQ(rf.fd(), uf.fd()); ASSERT_EQ(rf.path(), uf.path()); ASSERT_TRUE(rf.CreateBackupFile()); ASSERT_FILE_NOT_EXISTING(backupFile); rf.CommitWorkFile(); ASSERT_EQ(rf.fd(), uf.fd()); ASSERT_EQ(rf.path(), uf.path()); ASSERT_EQ(-1, rf.fd()); ASSERT_EQ(testFile, rf.path()); } } TEST_F(FileTest, TestRestorableFileNewFileNotCommitted) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_FILE_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_FILE_CONTENT(tmpFile, "NewContent"); } ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(testFile); } TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginal) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; CreateTestFileWithContents(testFile, "OriginalContent"); { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_FILE_CONTENT(tmpFile, "NewContent"); ASSERT_FILE_EXISTING(testFile); } ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_CONTENT(testFile, "OriginalContent"); } TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginalAndOldTmp) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; CreateTestFileWithContents(testFile, "OriginalContent"); CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp"); { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_FILE_CONTENT(tmpFile, "NewContent"); ASSERT_FILE_EXISTING(testFile); } ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_CONTENT(testFile, "OriginalContent"); } TEST_F(FileTest, TestRestorableFileNewFileCommitted) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; std::string backupFile = testFile + kBackupFileSuffix; { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_FILE_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_FILE_CONTENT(tmpFile, "NewContent"); ASSERT_TRUE(rf.CreateBackupFile()); ASSERT_FILE_NOT_EXISTING(backupFile); ASSERT_TRUE(rf.CommitWorkFile()); rf.RemoveBackupFile(); ASSERT_FILE_CONTENT(testFile, "NewContent"); } ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(backupFile); ASSERT_FILE_CONTENT(testFile, "NewContent"); } TEST_F(FileTest, TestRestorableFileCommittedWithOriginal) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; std::string backupFile = testFile + kBackupFileSuffix; CreateTestFileWithContents(testFile, "OriginalContent"); { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_FILE_CONTENT(tmpFile, "NewContent"); ASSERT_TRUE(rf.CreateBackupFile()); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_EXISTING(backupFile); ASSERT_TRUE(rf.CommitWorkFile()); ASSERT_FILE_EXISTING(backupFile); ASSERT_FILE_CONTENT(testFile, "NewContent"); rf.RemoveBackupFile(); ASSERT_FILE_NOT_EXISTING(backupFile); } ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_CONTENT(testFile, "NewContent"); } TEST_F(FileTest, TestRestorableFileCommittedWithOriginalAndOldTmp) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; CreateTestFileWithContents(testFile, "OriginalContent"); CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp"); { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_FILE_CONTENT(tmpFile, "NewContent"); ASSERT_TRUE(rf.CommitWorkFile()); ASSERT_FILE_CONTENT(testFile, "NewContent"); } ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_CONTENT(testFile, "NewContent"); } TEST_F(FileTest, TestRestorableFileCommitFailureNoOriginal) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; std::string backupFile = testFile + kBackupFileSuffix; { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_TRUE(rf.CreateBackupFile()); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(backupFile); // Now remove tmp file to force commit failure. close(rf.fd()); ASSERT_EQ(0, unlink(tmpFile.c_str())); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FALSE(rf.CommitWorkFile()); ASSERT_EQ(-1, rf.fd()); ASSERT_EQ(testFile, rf.path()); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_TRUE(rf.RestoreBackupFile()); } ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(backupFile); } TEST_F(FileTest, TestRestorableFileCommitFailureAndRollback) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; std::string backupFile = testFile + kBackupFileSuffix; CreateTestFileWithContents(testFile, "OriginalContent"); { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_TRUE(rf.CreateBackupFile()); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_EXISTING(backupFile); // Now remove tmp file to force commit failure. close(rf.fd()); ASSERT_EQ(0, unlink(tmpFile.c_str())); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FALSE(rf.CommitWorkFile()); ASSERT_EQ(-1, rf.fd()); ASSERT_EQ(testFile, rf.path()); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_EXISTING(backupFile); ASSERT_TRUE(rf.RestoreBackupFile()); } ASSERT_FILE_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(backupFile); } TEST_F(FileTest, TestRestorableFileResetAndRemoveAllFiles) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; std::string backupFile = testFile + kBackupFileSuffix; CreateTestFileWithContents(testFile, "OriginalContent"); { RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); ASSERT_TRUE(rf.CreateBackupFile()); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_EXISTING(backupFile); rf.ResetAndRemoveAllFiles(); ASSERT_EQ(-1, rf.fd()); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(backupFile); } ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(backupFile); } TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentFile) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; std::string backupFile = testFile + kBackupFileSuffix; CreateTestFileWithContents(testFile, "OriginalContent"); RestorableFile::RemoveAllFiles(testFile); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(backupFile); } TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentAndTmpFile) { std::string testFile = GetTestFilePath(GetTestName()); std::string tmpFile = testFile + kTmpFileSuffix; std::string backupFile = testFile + kBackupFileSuffix; CreateTestFileWithContents(testFile, "OriginalContent"); CreateTestFileWithContents(testFile + kTmpFileSuffix, "TmpContent"); RestorableFile::RemoveAllFiles(testFile); ASSERT_FILE_NOT_EXISTING(tmpFile); ASSERT_FILE_NOT_EXISTING(testFile); ASSERT_FILE_NOT_EXISTING(backupFile); } } // namespace installd } // namespace android