Loading fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +0 −23 Original line number Diff line number Diff line Loading @@ -214,29 +214,6 @@ void SetSize(PartitionUpdate* partition_update, uint64_t size); // Get partition size from update package metadata. uint64_t GetSize(PartitionUpdate* partition_update); // Util class for test cases on low space scenario. These tests assumes image manager // uses /data as backup device. class LowSpaceUserdata { public: // Set the maximum free space allowed for this test. If /userdata has more space than the given // number, a file is allocated to consume space. AssertionResult Init(uint64_t max_free_space); uint64_t free_space() const; uint64_t available_space() const; uint64_t bsize() const; private: AssertionResult ReadUserdataStats(); static constexpr const char* kUserDataDevice = "/data"; std::unique_ptr<TemporaryFile> big_file_; bool initialized_ = false; uint64_t free_space_ = 0; uint64_t available_space_ = 0; uint64_t bsize_ = 0; }; bool IsVirtualAbEnabled(); #define SKIP_IF_NON_VIRTUAL_AB() \ Loading fs_mgr/libsnapshot/snapshot.cpp +9 −2 Original line number Diff line number Diff line Loading @@ -3133,6 +3133,7 @@ static Return AddRequiredSpace(Return orig, for (auto&& [name, status] : all_snapshot_status) { sum += status.cow_file_size(); } LOG(INFO) << "Calculated needed COW space: " << sum << " bytes"; return Return::NoSpace(sum); } Loading Loading @@ -3279,7 +3280,10 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices, &all_snapshot_status); if (!ret.is_ok()) return ret; if (!ret.is_ok()) { LOG(ERROR) << "CreateUpdateSnapshotsInternal failed: " << ret.string(); return ret; } auto exported_target_metadata = target_metadata->Export(); if (exported_target_metadata == nullptr) { Loading Loading @@ -3496,7 +3500,10 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal( // Create the backing COW image if necessary. if (snapshot_status.cow_file_size() > 0) { auto ret = CreateCowImage(lock, name); if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status); if (!ret.is_ok()) { LOG(ERROR) << "CreateCowImage failed: " << ret.string(); return AddRequiredSpace(ret, *all_snapshot_status); } } LOG(INFO) << "Successfully created snapshot for " << name; Loading fs_mgr/libsnapshot/snapshot_test.cpp +46 −29 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <signal.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/statvfs.h> #include <sys/types.h> #include <chrono> Loading Loading @@ -2371,20 +2372,44 @@ TEST_F(SnapshotUpdateTest, Overflow) { << "FinishedSnapshotWrites should detect overflow of CoW device."; } // Get max file size and free space. std::pair<uint64_t, uint64_t> GetBigFileLimit() { struct statvfs fs; if (statvfs("/data", &fs) < 0) { PLOG(ERROR) << "statfs failed"; return {0, 0}; } auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1); auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize; LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free; return {fs_limit, fs_free}; } TEST_F(SnapshotUpdateTest, LowSpace) { static constexpr auto kMaxFree = 10_MiB; auto userdata = std::make_unique<LowSpaceUserdata>(); ASSERT_TRUE(userdata->Init(kMaxFree)); // To make the low space test more reliable, we force a large cow estimate. // However legacy VAB ignores the COW estimate and uses InstallOperations // to compute the exact size required for dm-snapshot. It's difficult to // make this work reliably (we'd need to somehow fake an extremely large // super partition, and we don't have that level of dependency injection). // // For now, just skip this test on legacy VAB. if (!snapuserd_required_) { GTEST_SKIP() << "Skipping test on legacy VAB"; } auto fs = GetBigFileLimit(); ASSERT_NE(fs.first, 0); // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space. constexpr uint64_t partition_size = 10_MiB; SetSize(sys_, partition_size); SetSize(vnd_, partition_size); SetSize(prd_, partition_size); sys_->set_estimate_cow_size(partition_size); vnd_->set_estimate_cow_size(partition_size); prd_->set_estimate_cow_size(partition_size); sys_->set_estimate_cow_size(fs.first); vnd_->set_estimate_cow_size(fs.first); prd_->set_estimate_cow_size(fs.first); AddOperationForPartitions(); Loading @@ -2393,8 +2418,12 @@ TEST_F(SnapshotUpdateTest, LowSpace) { auto res = sm->CreateUpdateSnapshots(manifest_); ASSERT_FALSE(res); ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code()); ASSERT_GE(res.required_size(), 14_MiB); ASSERT_LT(res.required_size(), 40_MiB); // It's hard to predict exactly how much free space is needed, since /data // is writable and the test is not the only process running. Divide by two // as a rough lower bound, and adjust this in the future as necessary. auto expected_delta = fs.first - fs.second; ASSERT_GE(res.required_size(), expected_delta / 2); } TEST_F(SnapshotUpdateTest, AddPartition) { Loading Loading @@ -2776,6 +2805,7 @@ class ImageManagerTest : public SnapshotTest { void TearDown() override { RETURN_IF_NON_VIRTUAL_AB(); CleanUp(); SnapshotTest::TearDown(); } void CleanUp() { if (!image_manager_) { Loading @@ -2789,28 +2819,15 @@ class ImageManagerTest : public SnapshotTest { }; TEST_F(ImageManagerTest, CreateImageNoSpace) { bool at_least_one_failure = false; for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) { auto userdata = std::make_unique<LowSpaceUserdata>(); ASSERT_TRUE(userdata->Init(size)); uint64_t to_allocate = userdata->free_space() + userdata->bsize(); auto fs = GetBigFileLimit(); ASSERT_NE(fs.first, 0); auto res = image_manager_->CreateBackingImage(kImageName, to_allocate, auto res = image_manager_->CreateBackingImage(kImageName, fs.first, IImageManager::CREATE_IMAGE_DEFAULT); if (!res) { at_least_one_failure = true; } else { ASSERT_FALSE(res); ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string(); } CleanUp(); } ASSERT_TRUE(at_least_one_failure) << "We should have failed to allocate at least one over-sized image"; } bool Mkdir(const std::string& path) { if (mkdir(path.c_str(), 0700) && errno != EEXIST) { std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl; Loading fs_mgr/libsnapshot/test_helpers.cpp +0 −62 Original line number Diff line number Diff line Loading @@ -214,68 +214,6 @@ uint64_t GetSize(PartitionUpdate* partition_update) { return partition_update->mutable_new_partition_info()->size(); } AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) { auto res = ReadUserdataStats(); if (!res) return res; // Try to fill up the disk as much as possible until free_space_ <= max_free_space. big_file_ = std::make_unique<TemporaryFile>(); if (big_file_->fd == -1) { return AssertionFailure() << strerror(errno); } if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) { return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in " << kUserDataDevice; } uint64_t next_consume = std::min(std::max(available_space_, max_free_space) - max_free_space, (uint64_t)std::numeric_limits<off_t>::max()); off_t allocated = 0; while (next_consume > 0 && free_space_ > max_free_space) { int status = fallocate(big_file_->fd, 0, allocated, next_consume); if (status == -1 && errno == ENOSPC) { next_consume /= 2; continue; } if (status == -1) { return AssertionFailure() << strerror(errno); } allocated += next_consume; res = ReadUserdataStats(); if (!res) return res; } LOG(INFO) << allocated << " bytes allocated to " << big_file_->path; initialized_ = true; return AssertionSuccess(); } AssertionResult LowSpaceUserdata::ReadUserdataStats() { struct statvfs buf; if (statvfs(kUserDataDevice, &buf) == -1) { return AssertionFailure() << strerror(errno); } bsize_ = buf.f_bsize; free_space_ = bsize_ * buf.f_bfree; available_space_ = bsize_ * buf.f_bavail; return AssertionSuccess(); } uint64_t LowSpaceUserdata::free_space() const { CHECK(initialized_); return free_space_; } uint64_t LowSpaceUserdata::available_space() const { CHECK(initialized_); return available_space_; } uint64_t LowSpaceUserdata::bsize() const { CHECK(initialized_); return bsize_; } bool IsVirtualAbEnabled() { return android::base::GetBoolProperty("ro.virtual_ab.enabled", false); } Loading Loading
fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +0 −23 Original line number Diff line number Diff line Loading @@ -214,29 +214,6 @@ void SetSize(PartitionUpdate* partition_update, uint64_t size); // Get partition size from update package metadata. uint64_t GetSize(PartitionUpdate* partition_update); // Util class for test cases on low space scenario. These tests assumes image manager // uses /data as backup device. class LowSpaceUserdata { public: // Set the maximum free space allowed for this test. If /userdata has more space than the given // number, a file is allocated to consume space. AssertionResult Init(uint64_t max_free_space); uint64_t free_space() const; uint64_t available_space() const; uint64_t bsize() const; private: AssertionResult ReadUserdataStats(); static constexpr const char* kUserDataDevice = "/data"; std::unique_ptr<TemporaryFile> big_file_; bool initialized_ = false; uint64_t free_space_ = 0; uint64_t available_space_ = 0; uint64_t bsize_ = 0; }; bool IsVirtualAbEnabled(); #define SKIP_IF_NON_VIRTUAL_AB() \ Loading
fs_mgr/libsnapshot/snapshot.cpp +9 −2 Original line number Diff line number Diff line Loading @@ -3133,6 +3133,7 @@ static Return AddRequiredSpace(Return orig, for (auto&& [name, status] : all_snapshot_status) { sum += status.cow_file_size(); } LOG(INFO) << "Calculated needed COW space: " << sum << " bytes"; return Return::NoSpace(sum); } Loading Loading @@ -3279,7 +3280,10 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices, &all_snapshot_status); if (!ret.is_ok()) return ret; if (!ret.is_ok()) { LOG(ERROR) << "CreateUpdateSnapshotsInternal failed: " << ret.string(); return ret; } auto exported_target_metadata = target_metadata->Export(); if (exported_target_metadata == nullptr) { Loading Loading @@ -3496,7 +3500,10 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal( // Create the backing COW image if necessary. if (snapshot_status.cow_file_size() > 0) { auto ret = CreateCowImage(lock, name); if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status); if (!ret.is_ok()) { LOG(ERROR) << "CreateCowImage failed: " << ret.string(); return AddRequiredSpace(ret, *all_snapshot_status); } } LOG(INFO) << "Successfully created snapshot for " << name; Loading
fs_mgr/libsnapshot/snapshot_test.cpp +46 −29 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <signal.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/statvfs.h> #include <sys/types.h> #include <chrono> Loading Loading @@ -2371,20 +2372,44 @@ TEST_F(SnapshotUpdateTest, Overflow) { << "FinishedSnapshotWrites should detect overflow of CoW device."; } // Get max file size and free space. std::pair<uint64_t, uint64_t> GetBigFileLimit() { struct statvfs fs; if (statvfs("/data", &fs) < 0) { PLOG(ERROR) << "statfs failed"; return {0, 0}; } auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1); auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize; LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free; return {fs_limit, fs_free}; } TEST_F(SnapshotUpdateTest, LowSpace) { static constexpr auto kMaxFree = 10_MiB; auto userdata = std::make_unique<LowSpaceUserdata>(); ASSERT_TRUE(userdata->Init(kMaxFree)); // To make the low space test more reliable, we force a large cow estimate. // However legacy VAB ignores the COW estimate and uses InstallOperations // to compute the exact size required for dm-snapshot. It's difficult to // make this work reliably (we'd need to somehow fake an extremely large // super partition, and we don't have that level of dependency injection). // // For now, just skip this test on legacy VAB. if (!snapuserd_required_) { GTEST_SKIP() << "Skipping test on legacy VAB"; } auto fs = GetBigFileLimit(); ASSERT_NE(fs.first, 0); // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space. constexpr uint64_t partition_size = 10_MiB; SetSize(sys_, partition_size); SetSize(vnd_, partition_size); SetSize(prd_, partition_size); sys_->set_estimate_cow_size(partition_size); vnd_->set_estimate_cow_size(partition_size); prd_->set_estimate_cow_size(partition_size); sys_->set_estimate_cow_size(fs.first); vnd_->set_estimate_cow_size(fs.first); prd_->set_estimate_cow_size(fs.first); AddOperationForPartitions(); Loading @@ -2393,8 +2418,12 @@ TEST_F(SnapshotUpdateTest, LowSpace) { auto res = sm->CreateUpdateSnapshots(manifest_); ASSERT_FALSE(res); ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code()); ASSERT_GE(res.required_size(), 14_MiB); ASSERT_LT(res.required_size(), 40_MiB); // It's hard to predict exactly how much free space is needed, since /data // is writable and the test is not the only process running. Divide by two // as a rough lower bound, and adjust this in the future as necessary. auto expected_delta = fs.first - fs.second; ASSERT_GE(res.required_size(), expected_delta / 2); } TEST_F(SnapshotUpdateTest, AddPartition) { Loading Loading @@ -2776,6 +2805,7 @@ class ImageManagerTest : public SnapshotTest { void TearDown() override { RETURN_IF_NON_VIRTUAL_AB(); CleanUp(); SnapshotTest::TearDown(); } void CleanUp() { if (!image_manager_) { Loading @@ -2789,28 +2819,15 @@ class ImageManagerTest : public SnapshotTest { }; TEST_F(ImageManagerTest, CreateImageNoSpace) { bool at_least_one_failure = false; for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) { auto userdata = std::make_unique<LowSpaceUserdata>(); ASSERT_TRUE(userdata->Init(size)); uint64_t to_allocate = userdata->free_space() + userdata->bsize(); auto fs = GetBigFileLimit(); ASSERT_NE(fs.first, 0); auto res = image_manager_->CreateBackingImage(kImageName, to_allocate, auto res = image_manager_->CreateBackingImage(kImageName, fs.first, IImageManager::CREATE_IMAGE_DEFAULT); if (!res) { at_least_one_failure = true; } else { ASSERT_FALSE(res); ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string(); } CleanUp(); } ASSERT_TRUE(at_least_one_failure) << "We should have failed to allocate at least one over-sized image"; } bool Mkdir(const std::string& path) { if (mkdir(path.c_str(), 0700) && errno != EEXIST) { std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl; Loading
fs_mgr/libsnapshot/test_helpers.cpp +0 −62 Original line number Diff line number Diff line Loading @@ -214,68 +214,6 @@ uint64_t GetSize(PartitionUpdate* partition_update) { return partition_update->mutable_new_partition_info()->size(); } AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) { auto res = ReadUserdataStats(); if (!res) return res; // Try to fill up the disk as much as possible until free_space_ <= max_free_space. big_file_ = std::make_unique<TemporaryFile>(); if (big_file_->fd == -1) { return AssertionFailure() << strerror(errno); } if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) { return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in " << kUserDataDevice; } uint64_t next_consume = std::min(std::max(available_space_, max_free_space) - max_free_space, (uint64_t)std::numeric_limits<off_t>::max()); off_t allocated = 0; while (next_consume > 0 && free_space_ > max_free_space) { int status = fallocate(big_file_->fd, 0, allocated, next_consume); if (status == -1 && errno == ENOSPC) { next_consume /= 2; continue; } if (status == -1) { return AssertionFailure() << strerror(errno); } allocated += next_consume; res = ReadUserdataStats(); if (!res) return res; } LOG(INFO) << allocated << " bytes allocated to " << big_file_->path; initialized_ = true; return AssertionSuccess(); } AssertionResult LowSpaceUserdata::ReadUserdataStats() { struct statvfs buf; if (statvfs(kUserDataDevice, &buf) == -1) { return AssertionFailure() << strerror(errno); } bsize_ = buf.f_bsize; free_space_ = bsize_ * buf.f_bfree; available_space_ = bsize_ * buf.f_bavail; return AssertionSuccess(); } uint64_t LowSpaceUserdata::free_space() const { CHECK(initialized_); return free_space_; } uint64_t LowSpaceUserdata::available_space() const { CHECK(initialized_); return available_space_; } uint64_t LowSpaceUserdata::bsize() const { CHECK(initialized_); return bsize_; } bool IsVirtualAbEnabled() { return android::base::GetBoolProperty("ro.virtual_ab.enabled", false); } Loading