blob: 00fb30875f51dab24d19c2952f26844f1cb1f4b5 [file] [log] [blame]
/*
* 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 <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <stdlib.h>
#include <string.h>
#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