Merge "SnapshotManager expose no space error"
This commit is contained in:
commit
3be1310cf8
6 changed files with 131 additions and 58 deletions
|
|
@ -56,10 +56,12 @@ class FiemapStatus {
|
||||||
// For logging and debugging only.
|
// For logging and debugging only.
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FiemapStatus(ErrorCode code) : error_code_(code) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorCode error_code_;
|
ErrorCode error_code_;
|
||||||
|
|
||||||
FiemapStatus(ErrorCode code) : error_code_(code) {}
|
|
||||||
static ErrorCode CastErrorCode(int error);
|
static ErrorCode CastErrorCode(int error);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,30 @@ class SnapshotManager final {
|
||||||
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
|
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
|
||||||
using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
|
using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
|
||||||
using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
|
using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
|
||||||
|
using FiemapStatus = android::fiemap::FiemapStatus;
|
||||||
|
|
||||||
public:
|
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.
|
// Dependency injection for testing.
|
||||||
class IDeviceInfo {
|
class IDeviceInfo {
|
||||||
public:
|
public:
|
||||||
|
|
@ -222,7 +244,7 @@ class SnapshotManager final {
|
||||||
// Create necessary COW device / files for OTA clients. New logical partitions will be added to
|
// 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
|
// group "cow" in target_metadata. Regions of partitions of current_metadata will be
|
||||||
// "write-protected" and snapshotted.
|
// "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
|
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
|
||||||
// determined previously in CreateSnapshots.
|
// determined previously in CreateSnapshots.
|
||||||
|
|
@ -359,7 +381,7 @@ class SnapshotManager final {
|
||||||
|
|
||||||
// |name| should be the base partition name (e.g. "system_a"). Create the
|
// |name| should be the base partition name (e.g. "system_a"). Create the
|
||||||
// backing COW image using the size previously passed to CreateSnapshot().
|
// 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.
|
// Map a snapshot device that was previously created with CreateSnapshot.
|
||||||
// If a merge was previously initiated, the device-mapper table will have a
|
// If a merge was previously initiated, the device-mapper table will have a
|
||||||
|
|
@ -499,14 +521,14 @@ class SnapshotManager final {
|
||||||
|
|
||||||
// Helper for CreateUpdateSnapshots.
|
// Helper for CreateUpdateSnapshots.
|
||||||
// Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
|
// Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
|
||||||
bool CreateUpdateSnapshotsInternal(LockedFile* lock, const DeltaArchiveManifest& manifest,
|
Return CreateUpdateSnapshotsInternal(
|
||||||
PartitionCowCreator* cow_creator,
|
LockedFile* lock, const DeltaArchiveManifest& manifest,
|
||||||
AutoDeviceList* created_devices,
|
PartitionCowCreator* cow_creator, AutoDeviceList* created_devices,
|
||||||
std::map<std::string, SnapshotStatus>* all_snapshot_status);
|
std::map<std::string, SnapshotStatus>* all_snapshot_status);
|
||||||
|
|
||||||
// Initialize snapshots so that they can be mapped later.
|
// Initialize snapshots so that they can be mapped later.
|
||||||
// Map the COW partition and zero-initialize the header.
|
// Map the COW partition and zero-initialize the header.
|
||||||
bool InitializeUpdateSnapshots(
|
Return InitializeUpdateSnapshots(
|
||||||
LockedFile* lock, MetadataBuilder* target_metadata,
|
LockedFile* lock, MetadataBuilder* target_metadata,
|
||||||
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
|
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
|
||||||
const std::map<std::string, SnapshotStatus>& all_snapshot_status);
|
const std::map<std::string, SnapshotStatus>& all_snapshot_status);
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ using android::dm::DmTargetLinear;
|
||||||
using android::dm::DmTargetSnapshot;
|
using android::dm::DmTargetSnapshot;
|
||||||
using android::dm::kSectorSize;
|
using android::dm::kSectorSize;
|
||||||
using android::dm::SnapshotStorageMode;
|
using android::dm::SnapshotStorageMode;
|
||||||
|
using android::fiemap::FiemapStatus;
|
||||||
using android::fiemap::IImageManager;
|
using android::fiemap::IImageManager;
|
||||||
using android::fs_mgr::CreateDmTable;
|
using android::fs_mgr::CreateDmTable;
|
||||||
using android::fs_mgr::CreateLogicalPartition;
|
using android::fs_mgr::CreateLogicalPartition;
|
||||||
|
|
@ -289,14 +290,14 @@ bool SnapshotManager::CreateSnapshot(LockedFile* lock, SnapshotStatus* status) {
|
||||||
return true;
|
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);
|
||||||
CHECK(lock->lock_mode() == LOCK_EX);
|
CHECK(lock->lock_mode() == LOCK_EX);
|
||||||
if (!EnsureImageManager()) return false;
|
if (!EnsureImageManager()) return Return::Error();
|
||||||
|
|
||||||
SnapshotStatus status;
|
SnapshotStatus status;
|
||||||
if (!ReadSnapshotStatus(lock, name, &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.
|
// 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) {
|
if (status.cow_file_size() % kSectorSize != 0) {
|
||||||
LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
|
LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
|
||||||
<< status.cow_file_size();
|
<< status.cow_file_size();
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cow_image_name = GetCowImageDeviceName(name);
|
std::string cow_image_name = GetCowImageDeviceName(name);
|
||||||
int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
|
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,
|
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();
|
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
|
// 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
|
// 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()) {
|
if (device_->IsOverlayfsSetup()) {
|
||||||
LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
|
LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
|
||||||
<< ", reboot, then try again.";
|
<< ", reboot, then try again.";
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& opener = device_->GetPartitionOpener();
|
const auto& opener = device_->GetPartitionOpener();
|
||||||
|
|
@ -1879,7 +1894,7 @@ bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest
|
||||||
SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
|
SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
|
||||||
if (!metadata_updater.Update()) {
|
if (!metadata_updater.Update()) {
|
||||||
LOG(ERROR) << "Cannot calculate new metadata.";
|
LOG(ERROR) << "Cannot calculate new metadata.";
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete previous COW partitions in current_metadata so that PartitionCowCreator marks those as
|
// 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 = {},
|
.extra_extents = {},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
|
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
|
||||||
&all_snapshot_status)) {
|
&all_snapshot_status);
|
||||||
return false;
|
if (!ret.is_ok()) return ret;
|
||||||
}
|
|
||||||
|
|
||||||
auto exported_target_metadata = target_metadata->Export();
|
auto exported_target_metadata = target_metadata->Export();
|
||||||
if (exported_target_metadata == nullptr) {
|
if (exported_target_metadata == nullptr) {
|
||||||
LOG(ERROR) << "Cannot export target metadata";
|
LOG(ERROR) << "Cannot export target metadata";
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
|
ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
|
||||||
exported_target_metadata.get(), target_suffix,
|
exported_target_metadata.get(), target_suffix,
|
||||||
all_snapshot_status)) {
|
all_snapshot_status);
|
||||||
return false;
|
if (!ret.is_ok()) return ret;
|
||||||
}
|
|
||||||
|
|
||||||
if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
|
if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
|
||||||
*exported_target_metadata, target_slot)) {
|
*exported_target_metadata, target_slot)) {
|
||||||
LOG(ERROR) << "Cannot write target metadata";
|
LOG(ERROR) << "Cannot write target metadata";
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
created_devices.Release();
|
created_devices.Release();
|
||||||
LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
|
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,
|
LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,
|
||||||
AutoDeviceList* created_devices,
|
AutoDeviceList* created_devices,
|
||||||
std::map<std::string, SnapshotStatus>* all_snapshot_status) {
|
std::map<std::string, SnapshotStatus>* all_snapshot_status) {
|
||||||
|
|
@ -1951,7 +1964,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
|
||||||
|
|
||||||
if (!target_metadata->AddGroup(kCowGroupName, 0)) {
|
if (!target_metadata->AddGroup(kCowGroupName, 0)) {
|
||||||
LOG(ERROR) << "Cannot add group " << kCowGroupName;
|
LOG(ERROR) << "Cannot add group " << kCowGroupName;
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
|
std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
|
||||||
|
|
@ -1963,7 +1976,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
|
||||||
if (!inserted) {
|
if (!inserted) {
|
||||||
LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
|
LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
|
||||||
<< " in update manifest.";
|
<< " in update manifest.";
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& extra_extents = extra_extents_map[suffixed_name];
|
auto& extra_extents = extra_extents_map[suffixed_name];
|
||||||
|
|
@ -1992,7 +2005,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
|
||||||
// Compute the device sizes for the partition.
|
// Compute the device sizes for the partition.
|
||||||
auto cow_creator_ret = cow_creator->Run();
|
auto cow_creator_ret = cow_creator->Run();
|
||||||
if (!cow_creator_ret.has_value()) {
|
if (!cow_creator_ret.has_value()) {
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(INFO) << "For partition " << target_partition->name()
|
LOG(INFO) << "For partition " << target_partition->name()
|
||||||
|
|
@ -2006,7 +2019,7 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
|
||||||
if (!DeleteSnapshot(lock, target_partition->name())) {
|
if (!DeleteSnapshot(lock, target_partition->name())) {
|
||||||
LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
|
LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
|
||||||
<< target_partition->name();
|
<< target_partition->name();
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is possible that the whole partition uses free space in super, and snapshot / COW
|
// 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.
|
// Store these device sizes to snapshot status file.
|
||||||
if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
|
if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());
|
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()),
|
auto cow_partition = target_metadata->AddPartition(GetCowName(target_partition->name()),
|
||||||
kCowGroupName, 0 /* flags */);
|
kCowGroupName, 0 /* flags */);
|
||||||
if (cow_partition == nullptr) {
|
if (cow_partition == nullptr) {
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target_metadata->ResizePartition(
|
if (!target_metadata->ResizePartition(
|
||||||
|
|
@ -2046,28 +2059,34 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
|
||||||
cow_creator_ret->cow_partition_usable_regions)) {
|
cow_creator_ret->cow_partition_usable_regions)) {
|
||||||
LOG(ERROR) << "Cannot create COW partition on metadata with size "
|
LOG(ERROR) << "Cannot create COW partition on metadata with size "
|
||||||
<< cow_creator_ret->snapshot_status.cow_partition_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
|
// Only the in-memory target_metadata is modified; nothing to clean up if there is an
|
||||||
// error in the future.
|
// 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(),
|
all_snapshot_status->emplace(target_partition->name(),
|
||||||
std::move(cow_creator_ret->snapshot_status));
|
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,
|
LockedFile* lock, MetadataBuilder* target_metadata,
|
||||||
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
|
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
|
||||||
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
|
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
|
||||||
|
|
@ -2086,7 +2105,7 @@ bool SnapshotManager::InitializeUpdateSnapshots(
|
||||||
if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {
|
if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {
|
||||||
LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
|
LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
|
||||||
<< target_partition->name();
|
<< target_partition->name();
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = all_snapshot_status.find(target_partition->name());
|
auto it = all_snapshot_status.find(target_partition->name());
|
||||||
|
|
@ -2094,23 +2113,24 @@ bool SnapshotManager::InitializeUpdateSnapshots(
|
||||||
cow_params.partition_name = target_partition->name();
|
cow_params.partition_name = target_partition->name();
|
||||||
std::string cow_name;
|
std::string cow_name;
|
||||||
if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {
|
if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {
|
||||||
return false;
|
return Return::Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cow_path;
|
std::string cow_path;
|
||||||
if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
|
if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
|
||||||
LOG(ERROR) << "Cannot determine path for " << cow_name;
|
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() << ": "
|
LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
|
||||||
<< cow_path;
|
<< cow_path;
|
||||||
return false;
|
return AddRequiredSpace(ret, all_snapshot_status);
|
||||||
}
|
}
|
||||||
// Let destructor of created_devices_for_cow to unmap the COW devices.
|
// Let destructor of created_devices_for_cow to unmap the COW devices.
|
||||||
};
|
};
|
||||||
return true;
|
return Return::Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
|
bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
|
||||||
|
|
|
||||||
|
|
@ -1586,6 +1586,29 @@ TEST_F(SnapshotUpdateTest, WaitForMerge) {
|
||||||
ASSERT_THAT(merger.get(), AnyOf(UpdateState::None, UpdateState::MergeCompleted));
|
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,
|
class FlashAfterUpdateTest : public SnapshotUpdateTest,
|
||||||
public WithParamInterface<std::tuple<uint32_t, bool>> {
|
public WithParamInterface<std::tuple<uint32_t, bool>> {
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,15 @@
|
||||||
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include <android-base/file.h>
|
#include <android-base/file.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
#include <android-base/strings.h>
|
#include <android-base/strings.h>
|
||||||
#include <fs_mgr/roots.h>
|
#include <fs_mgr/roots.h>
|
||||||
|
|
||||||
using android::dm::kSectorSize;
|
using android::dm::kSectorSize;
|
||||||
|
using android::fiemap::FiemapStatus;
|
||||||
using android::fs_mgr::EnsurePathMounted;
|
using android::fs_mgr::EnsurePathMounted;
|
||||||
using android::fs_mgr::EnsurePathUnmounted;
|
using android::fs_mgr::EnsurePathUnmounted;
|
||||||
using android::fs_mgr::Fstab;
|
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
|
// When the kernel creates a persistent dm-snapshot, it requires a CoW file
|
||||||
// to store the modifications. The kernel interface does not specify how
|
// to store the modifications. The kernel interface does not specify how
|
||||||
// the CoW is used, and there is no standard associated.
|
// 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));
|
android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
PLOG(ERROR) << "Can't open COW device: " << device;
|
PLOG(ERROR) << "Can't open COW device: " << device;
|
||||||
return false;
|
return Return(FiemapStatus::FromErrno(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(INFO) << "Zero-filling COW device: " << device;
|
LOG(INFO) << "Zero-filling COW device: " << device;
|
||||||
if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
|
if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
|
||||||
PLOG(ERROR) << "Can't zero-fill COW device for " << device;
|
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) {
|
std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include <update_engine/update_metadata.pb.h>
|
#include <update_engine/update_metadata.pb.h>
|
||||||
|
|
||||||
#include <libsnapshot/auto_device.h>
|
#include <libsnapshot/auto_device.h>
|
||||||
|
#include <libsnapshot/snapshot.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace snapshot {
|
namespace snapshot {
|
||||||
|
|
@ -110,7 +111,7 @@ std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
|
||||||
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
|
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
|
||||||
|
|
||||||
// Initialize a device before using it as the COW device for a dm-snapshot device.
|
// 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:
|
// "Atomically" write string to file. This is done by a series of actions:
|
||||||
// 1. Write to path + ".tmp"
|
// 1. Write to path + ".tmp"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue