Loading fs_mgr/libdm/include/libdm/dm.h +1 −0 Original line number Diff line number Diff line Loading @@ -197,6 +197,7 @@ class DeviceMapper final { struct TargetInfo { struct dm_target_spec spec; std::string data; TargetInfo() {} TargetInfo(const struct dm_target_spec& spec, const std::string& data) : spec(spec), data(data) {} }; Loading fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +65 −10 Original line number Diff line number Diff line Loading @@ -22,7 +22,8 @@ #include <vector> #include <android-base/unique_fd.h> #include <libdm/dm_target.h> #include <libdm/dm.h> #include <libfiemap/image_manager.h> #ifndef FRIEND_TEST #define FRIEND_TEST(test_set_name, individual_test) \ Loading @@ -38,7 +39,7 @@ class IImageManager; namespace snapshot { enum class UpdateState { enum class UpdateState : unsigned int { // No update or merge is in progress. None, Loading @@ -51,6 +52,10 @@ enum class UpdateState { // The kernel is merging in the background. Merging, // Post-merge cleanup steps could not be completed due to a transient // error, but the next reboot will finish any pending operations. MergeNeedsReboot, // Merging is complete, and needs to be acknowledged. MergeCompleted, Loading Loading @@ -94,8 +99,23 @@ class SnapshotManager final { // Wait for the current merge to finish, then perform cleanup when it // completes. It is necessary to call this after InitiateMerge(), or when // a merge is detected for the first time after boot. bool WaitForMerge(); // a merge state is detected during boot. // // Note that after calling WaitForMerge(), GetUpdateState() may still return // that a merge is in progress: // MergeFailed indicates that a fatal error occurred. WaitForMerge() may // called any number of times again to attempt to make more progress, but // we do not expect it to succeed if a catastrophic error occurred. // // MergeNeedsReboot indicates that the merge has completed, but cleanup // failed. This can happen if for some reason resources were not closed // properly. In this case another reboot is needed before we can take // another OTA. However, WaitForMerge() can be called again without // rebooting, to attempt to finish cleanup anyway. // // MergeCompleted indicates that the update has fully completed. // GetUpdateState will return None, and a new update can begin. UpdateState WaitForMerge(); // Find the status of the current update, if any. // Loading @@ -109,9 +129,14 @@ class SnapshotManager final { FRIEND_TEST(SnapshotTest, CreateSnapshot); FRIEND_TEST(SnapshotTest, MapSnapshot); FRIEND_TEST(SnapshotTest, MapPartialSnapshot); FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot); FRIEND_TEST(SnapshotTest, Merge); FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow); friend class SnapshotTest; using DmTargetSnapshot = android::dm::DmTargetSnapshot; using IImageManager = android::fiemap::IImageManager; using TargetInfo = android::dm::DeviceMapper::TargetInfo; explicit SnapshotManager(IDeviceInfo* info); Loading @@ -126,16 +151,18 @@ class SnapshotManager final { // this. It also serves as a proof-of-lock for some functions. class LockedFile final { public: LockedFile(const std::string& path, android::base::unique_fd&& fd) : path_(path), fd_(std::move(fd)) {} LockedFile(const std::string& path, android::base::unique_fd&& fd, int lock_mode) : path_(path), fd_(std::move(fd)), lock_mode_(lock_mode) {} ~LockedFile(); const std::string& path() const { return path_; } int fd() const { return fd_; } int lock_mode() const { return lock_mode_; } private: std::string path_; android::base::unique_fd fd_; int lock_mode_; }; std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags); bool Truncate(LockedFile* file); Loading Loading @@ -189,6 +216,7 @@ class SnapshotManager final { std::unique_ptr<LockedFile> LockExclusive(); UpdateState ReadUpdateState(LockedFile* file); bool WriteUpdateState(LockedFile* file, UpdateState state); std::string GetStateFilePath() const; // This state is persisted per-snapshot in /metadata/ota/snapshots/. struct SnapshotStatus { Loading @@ -200,11 +228,38 @@ class SnapshotManager final { uint64_t metadata_sectors = 0; }; // Helpers for merging. bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name); bool RewriteSnapshotDeviceTable(const std::string& dm_name); bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name); void AcknowledgeMergeSuccess(LockedFile* lock); void AcknowledgeMergeFailure(); // Note that these require the name of the device containing the snapshot, // which may be the "inner" device. Use GetsnapshotDeviecName(). bool QuerySnapshotStatus(const std::string& dm_name, std::string* target_type, DmTargetSnapshot::Status* status); bool IsSnapshotDevice(const std::string& dm_name, TargetInfo* target = nullptr); // Internal callback for when merging is complete. bool OnSnapshotMergeComplete(LockedFile* lock, const std::string& name, const SnapshotStatus& status); bool CollapseSnapshotDevice(const std::string& name, const SnapshotStatus& status); // Only the following UpdateStates are used here: // UpdateState::Merging // UpdateState::MergeCompleted // UpdateState::MergeFailed // UpdateState::MergeNeedsReboot UpdateState CheckMergeState(); UpdateState CheckMergeState(LockedFile* lock); UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name); // Interact with status files under /metadata/ota/snapshots. std::unique_ptr<LockedFile> OpenSnapshotStatusFile(const std::string& name, int open_flags, int lock_flags); bool WriteSnapshotStatus(LockedFile* file, const SnapshotStatus& status); bool ReadSnapshotStatus(LockedFile* file, SnapshotStatus* status); bool WriteSnapshotStatus(LockedFile* lock, const std::string& name, const SnapshotStatus& status); bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status); std::string GetSnapshotStatusFilePath(const std::string& name); // Return the name of the device holding the "snapshot" or "snapshot-merge" // target. This may not be the final device presented via MapSnapshot(), if Loading fs_mgr/libsnapshot/snapshot.cpp +589 −48 File changed.Preview size limit exceeded, changes collapsed. Show changes fs_mgr/libsnapshot/snapshot_test.cpp +155 −7 Original line number Diff line number Diff line Loading @@ -22,13 +22,20 @@ #include <chrono> #include <iostream> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <gtest/gtest.h> #include <libdm/dm.h> #include <libfiemap/image_manager.h> namespace android { namespace snapshot { using android::base::unique_fd; using android::dm::DeviceMapper; using android::dm::DmDeviceState; using namespace std::chrono_literals; using namespace std::string_literals; Loading @@ -48,12 +55,15 @@ std::unique_ptr<SnapshotManager> sm; TestDeviceInfo* test_device = nullptr; class SnapshotTest : public ::testing::Test { public: SnapshotTest() : dm_(DeviceMapper::Instance()) {} protected: void SetUp() override { test_device->set_is_running_snapshot(false); if (sm->GetUpdateState() != UpdateState::None) { ASSERT_TRUE(sm->CancelUpdate()); CleanupTestArtifacts(); } ASSERT_TRUE(sm->BeginUpdate()); ASSERT_TRUE(sm->EnsureImageManager()); Loading @@ -65,13 +75,32 @@ class SnapshotTest : public ::testing::Test { void TearDown() override { lock_ = nullptr; if (sm->GetUpdateState() != UpdateState::None) { ASSERT_TRUE(sm->CancelUpdate()); CleanupTestArtifacts(); } void CleanupTestArtifacts() { // Normally cancelling inside a merge is not allowed. Since these // are tests, we don't care, destroy everything that might exist. std::vector<std::string> snapshots = {"test-snapshot"}; for (const auto& snapshot : snapshots) { DeleteSnapshotDevice(snapshot); temp_images_.emplace_back(snapshot + "-cow"); auto status_file = sm->GetSnapshotStatusFilePath(snapshot); android::base::RemoveFileIfExists(status_file); } // Remove all images. temp_images_.emplace_back("test-snapshot-cow"); for (const auto& temp_image : temp_images_) { image_manager_->UnmapImageDevice(temp_image); image_manager_->DeleteBackingImage(temp_image); } if (sm->GetUpdateState() != UpdateState::None) { auto state_file = sm->GetStateFilePath(); unlink(state_file.c_str()); } } bool AcquireLock() { Loading @@ -87,6 +116,17 @@ class SnapshotTest : public ::testing::Test { return image_manager_->MapImageDevice(name, 10s, path); } bool DeleteSnapshotDevice(const std::string& snapshot) { if (dm_.GetState(snapshot) != DmDeviceState::INVALID) { if (!dm_.DeleteDevice(snapshot)) return false; } if (dm_.GetState(snapshot + "-inner") != DmDeviceState::INVALID) { if (!dm_.DeleteDevice(snapshot + "-inner")) return false; } return true; } DeviceMapper& dm_; std::unique_ptr<SnapshotManager::LockedFile> lock_; std::vector<std::string> temp_images_; android::fiemap::IImageManager* image_manager_ = nullptr; Loading @@ -106,11 +146,8 @@ TEST_F(SnapshotTest, CreateSnapshot) { // Scope so delete can re-acquire the snapshot file lock. { auto file = sm->OpenSnapshotStatusFile("test-snapshot", O_RDONLY, LOCK_SH); ASSERT_NE(file, nullptr); SnapshotManager::SnapshotStatus status; ASSERT_TRUE(sm->ReadSnapshotStatus(file.get(), &status)); ASSERT_TRUE(sm->ReadSnapshotStatus(lock_.get(), "test-snapshot", &status)); ASSERT_EQ(status.state, "created"); ASSERT_EQ(status.device_size, kDeviceSize); ASSERT_EQ(status.snapshot_size, kDeviceSize); Loading Loading @@ -151,6 +188,117 @@ TEST_F(SnapshotTest, MapPartialSnapshot) { ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-")); } TEST_F(SnapshotTest, NoMergeBeforeReboot) { ASSERT_TRUE(AcquireLock()); // Set the state to Unverified, as if we finished an update. ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified)); // Release the lock. lock_ = nullptr; // Merge should fail, since we didn't mark the device as rebooted. ASSERT_FALSE(sm->InitiateMerge()); } TEST_F(SnapshotTest, Merge) { ASSERT_TRUE(AcquireLock()); static const uint64_t kDeviceSize = 1024 * 1024; ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize, kDeviceSize)); std::string base_device, snap_device; ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device)); ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); std::string test_string = "This is a test string."; { unique_fd fd(open(snap_device.c_str(), O_RDWR | O_CLOEXEC | O_SYNC)); ASSERT_GE(fd, 0); ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size())); } // Note: we know the name of the device is test-snapshot because we didn't // request a linear segment. DeviceMapper::TargetInfo target; ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target)); ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot"); // Set the state to Unverified, as if we finished an update. ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified)); // Release the lock. lock_ = nullptr; test_device->set_is_running_snapshot(true); ASSERT_TRUE(sm->InitiateMerge()); // The device should have been switched to a snapshot-merge target. ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target)); ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge"); // We should not be able to cancel an update now. ASSERT_FALSE(sm->CancelUpdate()); ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted); ASSERT_EQ(sm->GetUpdateState(), UpdateState::None); // The device should no longer be a snapshot or snapshot-merge. ASSERT_FALSE(sm->IsSnapshotDevice("test-snapshot")); // Test that we can read back the string we wrote to the snapshot. unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC)); ASSERT_GE(fd, 0); std::string buffer(test_string.size(), '\0'); ASSERT_TRUE(android::base::ReadFully(fd, buffer.data(), buffer.size())); ASSERT_EQ(test_string, buffer); } TEST_F(SnapshotTest, MergeCannotRemoveCow) { ASSERT_TRUE(AcquireLock()); static const uint64_t kDeviceSize = 1024 * 1024; ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize, kDeviceSize)); std::string base_device, snap_device; ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device)); ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); // Keep an open handle to the cow device. This should cause the merge to // be incomplete. auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow", ""); unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC)); ASSERT_GE(fd, 0); // Set the state to Unverified, as if we finished an update. ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified)); // Release the lock. lock_ = nullptr; test_device->set_is_running_snapshot(true); ASSERT_TRUE(sm->InitiateMerge()); // COW cannot be removed due to open fd, so expect a soft failure. ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeNeedsReboot); // Forcefully delete the snapshot device, so it looks like we just rebooted. ASSERT_TRUE(DeleteSnapshotDevice("test-snapshot")); // Map snapshot should fail now, because we're in a merge-complete state. ASSERT_TRUE(AcquireLock()); ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); // Release everything and now the merge should complete. fd = {}; lock_ = nullptr; ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted); } } // namespace snapshot } // namespace android Loading Loading
fs_mgr/libdm/include/libdm/dm.h +1 −0 Original line number Diff line number Diff line Loading @@ -197,6 +197,7 @@ class DeviceMapper final { struct TargetInfo { struct dm_target_spec spec; std::string data; TargetInfo() {} TargetInfo(const struct dm_target_spec& spec, const std::string& data) : spec(spec), data(data) {} }; Loading
fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +65 −10 Original line number Diff line number Diff line Loading @@ -22,7 +22,8 @@ #include <vector> #include <android-base/unique_fd.h> #include <libdm/dm_target.h> #include <libdm/dm.h> #include <libfiemap/image_manager.h> #ifndef FRIEND_TEST #define FRIEND_TEST(test_set_name, individual_test) \ Loading @@ -38,7 +39,7 @@ class IImageManager; namespace snapshot { enum class UpdateState { enum class UpdateState : unsigned int { // No update or merge is in progress. None, Loading @@ -51,6 +52,10 @@ enum class UpdateState { // The kernel is merging in the background. Merging, // Post-merge cleanup steps could not be completed due to a transient // error, but the next reboot will finish any pending operations. MergeNeedsReboot, // Merging is complete, and needs to be acknowledged. MergeCompleted, Loading Loading @@ -94,8 +99,23 @@ class SnapshotManager final { // Wait for the current merge to finish, then perform cleanup when it // completes. It is necessary to call this after InitiateMerge(), or when // a merge is detected for the first time after boot. bool WaitForMerge(); // a merge state is detected during boot. // // Note that after calling WaitForMerge(), GetUpdateState() may still return // that a merge is in progress: // MergeFailed indicates that a fatal error occurred. WaitForMerge() may // called any number of times again to attempt to make more progress, but // we do not expect it to succeed if a catastrophic error occurred. // // MergeNeedsReboot indicates that the merge has completed, but cleanup // failed. This can happen if for some reason resources were not closed // properly. In this case another reboot is needed before we can take // another OTA. However, WaitForMerge() can be called again without // rebooting, to attempt to finish cleanup anyway. // // MergeCompleted indicates that the update has fully completed. // GetUpdateState will return None, and a new update can begin. UpdateState WaitForMerge(); // Find the status of the current update, if any. // Loading @@ -109,9 +129,14 @@ class SnapshotManager final { FRIEND_TEST(SnapshotTest, CreateSnapshot); FRIEND_TEST(SnapshotTest, MapSnapshot); FRIEND_TEST(SnapshotTest, MapPartialSnapshot); FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot); FRIEND_TEST(SnapshotTest, Merge); FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow); friend class SnapshotTest; using DmTargetSnapshot = android::dm::DmTargetSnapshot; using IImageManager = android::fiemap::IImageManager; using TargetInfo = android::dm::DeviceMapper::TargetInfo; explicit SnapshotManager(IDeviceInfo* info); Loading @@ -126,16 +151,18 @@ class SnapshotManager final { // this. It also serves as a proof-of-lock for some functions. class LockedFile final { public: LockedFile(const std::string& path, android::base::unique_fd&& fd) : path_(path), fd_(std::move(fd)) {} LockedFile(const std::string& path, android::base::unique_fd&& fd, int lock_mode) : path_(path), fd_(std::move(fd)), lock_mode_(lock_mode) {} ~LockedFile(); const std::string& path() const { return path_; } int fd() const { return fd_; } int lock_mode() const { return lock_mode_; } private: std::string path_; android::base::unique_fd fd_; int lock_mode_; }; std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags); bool Truncate(LockedFile* file); Loading Loading @@ -189,6 +216,7 @@ class SnapshotManager final { std::unique_ptr<LockedFile> LockExclusive(); UpdateState ReadUpdateState(LockedFile* file); bool WriteUpdateState(LockedFile* file, UpdateState state); std::string GetStateFilePath() const; // This state is persisted per-snapshot in /metadata/ota/snapshots/. struct SnapshotStatus { Loading @@ -200,11 +228,38 @@ class SnapshotManager final { uint64_t metadata_sectors = 0; }; // Helpers for merging. bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name); bool RewriteSnapshotDeviceTable(const std::string& dm_name); bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name); void AcknowledgeMergeSuccess(LockedFile* lock); void AcknowledgeMergeFailure(); // Note that these require the name of the device containing the snapshot, // which may be the "inner" device. Use GetsnapshotDeviecName(). bool QuerySnapshotStatus(const std::string& dm_name, std::string* target_type, DmTargetSnapshot::Status* status); bool IsSnapshotDevice(const std::string& dm_name, TargetInfo* target = nullptr); // Internal callback for when merging is complete. bool OnSnapshotMergeComplete(LockedFile* lock, const std::string& name, const SnapshotStatus& status); bool CollapseSnapshotDevice(const std::string& name, const SnapshotStatus& status); // Only the following UpdateStates are used here: // UpdateState::Merging // UpdateState::MergeCompleted // UpdateState::MergeFailed // UpdateState::MergeNeedsReboot UpdateState CheckMergeState(); UpdateState CheckMergeState(LockedFile* lock); UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name); // Interact with status files under /metadata/ota/snapshots. std::unique_ptr<LockedFile> OpenSnapshotStatusFile(const std::string& name, int open_flags, int lock_flags); bool WriteSnapshotStatus(LockedFile* file, const SnapshotStatus& status); bool ReadSnapshotStatus(LockedFile* file, SnapshotStatus* status); bool WriteSnapshotStatus(LockedFile* lock, const std::string& name, const SnapshotStatus& status); bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status); std::string GetSnapshotStatusFilePath(const std::string& name); // Return the name of the device holding the "snapshot" or "snapshot-merge" // target. This may not be the final device presented via MapSnapshot(), if Loading
fs_mgr/libsnapshot/snapshot.cpp +589 −48 File changed.Preview size limit exceeded, changes collapsed. Show changes
fs_mgr/libsnapshot/snapshot_test.cpp +155 −7 Original line number Diff line number Diff line Loading @@ -22,13 +22,20 @@ #include <chrono> #include <iostream> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <gtest/gtest.h> #include <libdm/dm.h> #include <libfiemap/image_manager.h> namespace android { namespace snapshot { using android::base::unique_fd; using android::dm::DeviceMapper; using android::dm::DmDeviceState; using namespace std::chrono_literals; using namespace std::string_literals; Loading @@ -48,12 +55,15 @@ std::unique_ptr<SnapshotManager> sm; TestDeviceInfo* test_device = nullptr; class SnapshotTest : public ::testing::Test { public: SnapshotTest() : dm_(DeviceMapper::Instance()) {} protected: void SetUp() override { test_device->set_is_running_snapshot(false); if (sm->GetUpdateState() != UpdateState::None) { ASSERT_TRUE(sm->CancelUpdate()); CleanupTestArtifacts(); } ASSERT_TRUE(sm->BeginUpdate()); ASSERT_TRUE(sm->EnsureImageManager()); Loading @@ -65,13 +75,32 @@ class SnapshotTest : public ::testing::Test { void TearDown() override { lock_ = nullptr; if (sm->GetUpdateState() != UpdateState::None) { ASSERT_TRUE(sm->CancelUpdate()); CleanupTestArtifacts(); } void CleanupTestArtifacts() { // Normally cancelling inside a merge is not allowed. Since these // are tests, we don't care, destroy everything that might exist. std::vector<std::string> snapshots = {"test-snapshot"}; for (const auto& snapshot : snapshots) { DeleteSnapshotDevice(snapshot); temp_images_.emplace_back(snapshot + "-cow"); auto status_file = sm->GetSnapshotStatusFilePath(snapshot); android::base::RemoveFileIfExists(status_file); } // Remove all images. temp_images_.emplace_back("test-snapshot-cow"); for (const auto& temp_image : temp_images_) { image_manager_->UnmapImageDevice(temp_image); image_manager_->DeleteBackingImage(temp_image); } if (sm->GetUpdateState() != UpdateState::None) { auto state_file = sm->GetStateFilePath(); unlink(state_file.c_str()); } } bool AcquireLock() { Loading @@ -87,6 +116,17 @@ class SnapshotTest : public ::testing::Test { return image_manager_->MapImageDevice(name, 10s, path); } bool DeleteSnapshotDevice(const std::string& snapshot) { if (dm_.GetState(snapshot) != DmDeviceState::INVALID) { if (!dm_.DeleteDevice(snapshot)) return false; } if (dm_.GetState(snapshot + "-inner") != DmDeviceState::INVALID) { if (!dm_.DeleteDevice(snapshot + "-inner")) return false; } return true; } DeviceMapper& dm_; std::unique_ptr<SnapshotManager::LockedFile> lock_; std::vector<std::string> temp_images_; android::fiemap::IImageManager* image_manager_ = nullptr; Loading @@ -106,11 +146,8 @@ TEST_F(SnapshotTest, CreateSnapshot) { // Scope so delete can re-acquire the snapshot file lock. { auto file = sm->OpenSnapshotStatusFile("test-snapshot", O_RDONLY, LOCK_SH); ASSERT_NE(file, nullptr); SnapshotManager::SnapshotStatus status; ASSERT_TRUE(sm->ReadSnapshotStatus(file.get(), &status)); ASSERT_TRUE(sm->ReadSnapshotStatus(lock_.get(), "test-snapshot", &status)); ASSERT_EQ(status.state, "created"); ASSERT_EQ(status.device_size, kDeviceSize); ASSERT_EQ(status.snapshot_size, kDeviceSize); Loading Loading @@ -151,6 +188,117 @@ TEST_F(SnapshotTest, MapPartialSnapshot) { ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-")); } TEST_F(SnapshotTest, NoMergeBeforeReboot) { ASSERT_TRUE(AcquireLock()); // Set the state to Unverified, as if we finished an update. ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified)); // Release the lock. lock_ = nullptr; // Merge should fail, since we didn't mark the device as rebooted. ASSERT_FALSE(sm->InitiateMerge()); } TEST_F(SnapshotTest, Merge) { ASSERT_TRUE(AcquireLock()); static const uint64_t kDeviceSize = 1024 * 1024; ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize, kDeviceSize)); std::string base_device, snap_device; ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device)); ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); std::string test_string = "This is a test string."; { unique_fd fd(open(snap_device.c_str(), O_RDWR | O_CLOEXEC | O_SYNC)); ASSERT_GE(fd, 0); ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size())); } // Note: we know the name of the device is test-snapshot because we didn't // request a linear segment. DeviceMapper::TargetInfo target; ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target)); ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot"); // Set the state to Unverified, as if we finished an update. ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified)); // Release the lock. lock_ = nullptr; test_device->set_is_running_snapshot(true); ASSERT_TRUE(sm->InitiateMerge()); // The device should have been switched to a snapshot-merge target. ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target)); ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge"); // We should not be able to cancel an update now. ASSERT_FALSE(sm->CancelUpdate()); ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted); ASSERT_EQ(sm->GetUpdateState(), UpdateState::None); // The device should no longer be a snapshot or snapshot-merge. ASSERT_FALSE(sm->IsSnapshotDevice("test-snapshot")); // Test that we can read back the string we wrote to the snapshot. unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC)); ASSERT_GE(fd, 0); std::string buffer(test_string.size(), '\0'); ASSERT_TRUE(android::base::ReadFully(fd, buffer.data(), buffer.size())); ASSERT_EQ(test_string, buffer); } TEST_F(SnapshotTest, MergeCannotRemoveCow) { ASSERT_TRUE(AcquireLock()); static const uint64_t kDeviceSize = 1024 * 1024; ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize, kDeviceSize)); std::string base_device, snap_device; ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device)); ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); // Keep an open handle to the cow device. This should cause the merge to // be incomplete. auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow", ""); unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC)); ASSERT_GE(fd, 0); // Set the state to Unverified, as if we finished an update. ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified)); // Release the lock. lock_ = nullptr; test_device->set_is_running_snapshot(true); ASSERT_TRUE(sm->InitiateMerge()); // COW cannot be removed due to open fd, so expect a soft failure. ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeNeedsReboot); // Forcefully delete the snapshot device, so it looks like we just rebooted. ASSERT_TRUE(DeleteSnapshotDevice("test-snapshot")); // Map snapshot should fail now, because we're in a merge-complete state. ASSERT_TRUE(AcquireLock()); ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); // Release everything and now the merge should complete. fd = {}; lock_ = nullptr; ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted); } } // namespace snapshot } // namespace android Loading