Merge "SnapshotManager expose no space error" am: 3be1310cf8 am: 5cf2d3ecc0 am: 7bcdc7d4af

Change-Id: Ie74e0d7a81aa24a88a3632e4a4ed2e2f4a27b9e1
This commit is contained in:
Automerger Merge Worker 2020-01-08 23:00:11 +00:00
commit 76be7208e3
6 changed files with 131 additions and 58 deletions

View file

@ -56,10 +56,12 @@ class FiemapStatus {
// For logging and debugging only.
std::string string() const;
protected:
FiemapStatus(ErrorCode code) : error_code_(code) {}
private:
ErrorCode error_code_;
FiemapStatus(ErrorCode code) : error_code_(code) {}
static ErrorCode CastErrorCode(int error);
};

View file

@ -116,8 +116,30 @@ class SnapshotManager final {
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
using FiemapStatus = android::fiemap::FiemapStatus;
public:
// SnapshotManager functions return either bool or Return objects. "Return" types provides
// more information about the reason of the failure.
class Return : public FiemapStatus {
public:
// Total required size on /userdata.
uint64_t required_size() const { return required_size_; }
static Return Ok() { return Return(FiemapStatus::ErrorCode::SUCCESS); }
static Return Error() { return Return(FiemapStatus::ErrorCode::ERROR); }
static Return NoSpace(uint64_t size) {
return Return(FiemapStatus::ErrorCode::NO_SPACE, size);
}
// Does not set required_size_ properly even when status.error_code() == NO_SPACE.
explicit Return(const FiemapStatus& status) : Return(status.error_code()) {}
private:
uint64_t required_size_;
Return(FiemapStatus::ErrorCode code, uint64_t required_size = 0)
: FiemapStatus(code), required_size_(required_size) {}
};
// Dependency injection for testing.
class IDeviceInfo {
public:
@ -222,7 +244,7 @@ class SnapshotManager final {
// Create necessary COW device / files for OTA clients. New logical partitions will be added to
// group "cow" in target_metadata. Regions of partitions of current_metadata will be
// "write-protected" and snapshotted.
bool CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
// determined previously in CreateSnapshots.
@ -359,7 +381,7 @@ class SnapshotManager final {
// |name| should be the base partition name (e.g. "system_a"). Create the
// backing COW image using the size previously passed to CreateSnapshot().
bool CreateCowImage(LockedFile* lock, const std::string& name);
Return CreateCowImage(LockedFile* lock, const std::string& name);
// Map a snapshot device that was previously created with CreateSnapshot.
// If a merge was previously initiated, the device-mapper table will have a
@ -499,14 +521,14 @@ class SnapshotManager final {
// Helper for CreateUpdateSnapshots.
// Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
bool CreateUpdateSnapshotsInternal(LockedFile* lock, const DeltaArchiveManifest& manifest,
PartitionCowCreator* cow_creator,
AutoDeviceList* created_devices,
std::map<std::string, SnapshotStatus>* all_snapshot_status);
Return CreateUpdateSnapshotsInternal(
LockedFile* lock, const DeltaArchiveManifest& manifest,
PartitionCowCreator* cow_creator, AutoDeviceList* created_devices,
std::map<std::string, SnapshotStatus>* all_snapshot_status);
// Initialize snapshots so that they can be mapped later.
// Map the COW partition and zero-initialize the header.
bool InitializeUpdateSnapshots(
Return InitializeUpdateSnapshots(
LockedFile* lock, MetadataBuilder* target_metadata,
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status);

View file

@ -54,6 +54,7 @@ using android::dm::DmTargetLinear;
using android::dm::DmTargetSnapshot;
using android::dm::kSectorSize;
using android::dm::SnapshotStorageMode;
using android::fiemap::FiemapStatus;
using android::fiemap::IImageManager;
using android::fs_mgr::CreateDmTable;
using android::fs_mgr::CreateLogicalPartition;
@ -289,14 +290,14 @@ bool SnapshotManager::CreateSnapshot(LockedFile* lock, SnapshotStatus* status) {
return true;
}
bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
SnapshotManager::Return SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
CHECK(lock);
CHECK(lock->lock_mode() == LOCK_EX);
if (!EnsureImageManager()) return false;
if (!EnsureImageManager()) return Return::Error();
SnapshotStatus status;
if (!ReadSnapshotStatus(lock, name, &status)) {
return false;
return Return::Error();
}
// The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
@ -304,12 +305,12 @@ bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name)
if (status.cow_file_size() % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
<< status.cow_file_size();
return false;
return Return::Error();
}
std::string cow_image_name = GetCowImageDeviceName(name);
int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
return images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags);
return Return(images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags));
}
bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
@ -1844,9 +1845,23 @@ static void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {
}
}
bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
static SnapshotManager::Return AddRequiredSpace(
SnapshotManager::Return orig,
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
if (orig.error_code() != SnapshotManager::Return::ErrorCode::NO_SPACE) {
return orig;
}
uint64_t sum = 0;
for (auto&& [name, status] : all_snapshot_status) {
sum += status.cow_file_size();
}
return SnapshotManager::Return::NoSpace(sum);
}
SnapshotManager::Return SnapshotManager::CreateUpdateSnapshots(
const DeltaArchiveManifest& manifest) {
auto lock = LockExclusive();
if (!lock) return false;
if (!lock) return Return::Error();
// TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
// partition takes up a big chunk of space in super, causing COW images to be created on
@ -1854,7 +1869,7 @@ bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest
if (device_->IsOverlayfsSetup()) {
LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
<< ", reboot, then try again.";
return false;
return Return::Error();
}
const auto& opener = device_->GetPartitionOpener();
@ -1879,7 +1894,7 @@ bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest
SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
if (!metadata_updater.Update()) {
LOG(ERROR) << "Cannot calculate new metadata.";
return false;
return Return::Error();
}
// Delete previous COW partitions in current_metadata so that PartitionCowCreator marks those as
@ -1911,36 +1926,34 @@ bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest
.extra_extents = {},
};
if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
&all_snapshot_status)) {
return false;
}
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
&all_snapshot_status);
if (!ret.is_ok()) return ret;
auto exported_target_metadata = target_metadata->Export();
if (exported_target_metadata == nullptr) {
LOG(ERROR) << "Cannot export target metadata";
return false;
return Return::Error();
}
if (!InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
exported_target_metadata.get(), target_suffix,
all_snapshot_status)) {
return false;
}
ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
exported_target_metadata.get(), target_suffix,
all_snapshot_status);
if (!ret.is_ok()) return ret;
if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
*exported_target_metadata, target_slot)) {
LOG(ERROR) << "Cannot write target metadata";
return false;
return Return::Error();
}
created_devices.Release();
LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
return true;
return Return::Ok();
}
bool SnapshotManager::CreateUpdateSnapshotsInternal(
SnapshotManager::Return SnapshotManager::CreateUpdateSnapshotsInternal(
LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,
AutoDeviceList* created_devices,
std::map<std::string, SnapshotStatus>* all_snapshot_status) {
@ -1951,7 +1964,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
if (!target_metadata->AddGroup(kCowGroupName, 0)) {
LOG(ERROR) << "Cannot add group " << kCowGroupName;
return false;
return Return::Error();
}
std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
@ -1963,7 +1976,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
if (!inserted) {
LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
<< " in update manifest.";
return false;
return Return::Error();
}
auto& extra_extents = extra_extents_map[suffixed_name];
@ -1992,7 +2005,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
// Compute the device sizes for the partition.
auto cow_creator_ret = cow_creator->Run();
if (!cow_creator_ret.has_value()) {
return false;
return Return::Error();
}
LOG(INFO) << "For partition " << target_partition->name()
@ -2006,7 +2019,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
if (!DeleteSnapshot(lock, target_partition->name())) {
LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
<< target_partition->name();
return false;
return Return::Error();
}
// It is possible that the whole partition uses free space in super, and snapshot / COW
@ -2024,7 +2037,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
// Store these device sizes to snapshot status file.
if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
return false;
return Return::Error();
}
created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());
@ -2038,7 +2051,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
auto cow_partition = target_metadata->AddPartition(GetCowName(target_partition->name()),
kCowGroupName, 0 /* flags */);
if (cow_partition == nullptr) {
return false;
return Return::Error();
}
if (!target_metadata->ResizePartition(
@ -2046,28 +2059,34 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
cow_creator_ret->cow_partition_usable_regions)) {
LOG(ERROR) << "Cannot create COW partition on metadata with size "
<< cow_creator_ret->snapshot_status.cow_partition_size();
return false;
return Return::Error();
}
// Only the in-memory target_metadata is modified; nothing to clean up if there is an
// error in the future.
}
// Create the backing COW image if necessary.
if (cow_creator_ret->snapshot_status.cow_file_size() > 0) {
if (!CreateCowImage(lock, target_partition->name())) {
return false;
}
}
all_snapshot_status->emplace(target_partition->name(),
std::move(cow_creator_ret->snapshot_status));
LOG(INFO) << "Successfully created snapshot for " << target_partition->name();
LOG(INFO) << "Successfully created snapshot partition for " << target_partition->name();
}
return true;
LOG(INFO) << "Allocating CoW images.";
for (auto&& [name, snapshot_status] : *all_snapshot_status) {
// 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);
}
LOG(INFO) << "Successfully created snapshot for " << name;
}
return Return::Ok();
}
bool SnapshotManager::InitializeUpdateSnapshots(
SnapshotManager::Return SnapshotManager::InitializeUpdateSnapshots(
LockedFile* lock, MetadataBuilder* target_metadata,
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
@ -2086,7 +2105,7 @@ bool SnapshotManager::InitializeUpdateSnapshots(
if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {
LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
<< target_partition->name();
return false;
return Return::Error();
}
auto it = all_snapshot_status.find(target_partition->name());
@ -2094,23 +2113,24 @@ bool SnapshotManager::InitializeUpdateSnapshots(
cow_params.partition_name = target_partition->name();
std::string cow_name;
if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {
return false;
return Return::Error();
}
std::string cow_path;
if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
LOG(ERROR) << "Cannot determine path for " << cow_name;
return false;
return Return::Error();
}
if (!InitializeCow(cow_path)) {
auto ret = InitializeCow(cow_path);
if (!ret.is_ok()) {
LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
<< cow_path;
return false;
return AddRequiredSpace(ret, all_snapshot_status);
}
// Let destructor of created_devices_for_cow to unmap the COW devices.
};
return true;
return Return::Ok();
}
bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,

View file

@ -1586,6 +1586,29 @@ TEST_F(SnapshotUpdateTest, WaitForMerge) {
ASSERT_THAT(merger.get(), AnyOf(UpdateState::None, UpdateState::MergeCompleted));
}
TEST_F(SnapshotUpdateTest, LowSpace) {
static constexpr auto kMaxFree = 10_MiB;
auto userdata = std::make_unique<LowSpaceUserdata>();
ASSERT_TRUE(userdata->Init(kMaxFree));
// Grow all partitions to 5_MiB, total 15_MiB. This requires 15 MiB of CoW space. After
// using the empty space in super (< 1 MiB), it uses at least 14 MiB of /userdata space.
constexpr uint64_t partition_size = 5_MiB;
SetSize(sys_, partition_size);
SetSize(vnd_, partition_size);
SetSize(prd_, partition_size);
AddOperationForPartitions();
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
auto res = sm->CreateUpdateSnapshots(manifest_);
ASSERT_FALSE(res);
ASSERT_EQ(SnapshotManager::Return::ErrorCode::NO_SPACE, res.error_code());
ASSERT_GE(res.required_size(), 14_MiB);
ASSERT_LT(res.required_size(), 15_MiB);
}
class FlashAfterUpdateTest : public SnapshotUpdateTest,
public WithParamInterface<std::tuple<uint32_t, bool>> {
public:

View file

@ -14,12 +14,15 @@
#include "utility.h"
#include <errno.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <fs_mgr/roots.h>
using android::dm::kSectorSize;
using android::fiemap::FiemapStatus;
using android::fs_mgr::EnsurePathMounted;
using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Fstab;
@ -83,7 +86,9 @@ AutoDeleteSnapshot::~AutoDeleteSnapshot() {
}
}
bool InitializeCow(const std::string& device) {
SnapshotManager::Return InitializeCow(const std::string& device) {
using Return = SnapshotManager::Return;
// When the kernel creates a persistent dm-snapshot, it requires a CoW file
// to store the modifications. The kernel interface does not specify how
// the CoW is used, and there is no standard associated.
@ -103,15 +108,15 @@ bool InitializeCow(const std::string& device) {
android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
if (fd < 0) {
PLOG(ERROR) << "Can't open COW device: " << device;
return false;
return Return(FiemapStatus::FromErrno(errno));
}
LOG(INFO) << "Zero-filling COW device: " << device;
if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
PLOG(ERROR) << "Can't zero-fill COW device for " << device;
return false;
return Return(FiemapStatus::FromErrno(errno));
}
return true;
return Return::Ok();
}
std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {

View file

@ -26,6 +26,7 @@
#include <update_engine/update_metadata.pb.h>
#include <libsnapshot/auto_device.h>
#include <libsnapshot/snapshot.h>
namespace android {
namespace snapshot {
@ -110,7 +111,7 @@ std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
// Initialize a device before using it as the COW device for a dm-snapshot device.
bool InitializeCow(const std::string& device);
SnapshotManager::Return InitializeCow(const std::string& device);
// "Atomically" write string to file. This is done by a series of actions:
// 1. Write to path + ".tmp"