Merge "libsnapshot_cow: Prepare device to boot from pre-created snapshots" into main
This commit is contained in:
commit
6b78aac2cf
4 changed files with 252 additions and 34 deletions
|
@ -73,6 +73,9 @@ class ISnapshotMergeStats;
|
|||
class SnapshotMergeStats;
|
||||
class SnapshotStatus;
|
||||
|
||||
using std::chrono::duration_cast;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static constexpr const std::string_view kCowGroupName = "cow";
|
||||
static constexpr char kVirtualAbCompressionProp[] = "ro.virtual_ab.compression.enabled";
|
||||
|
||||
|
@ -424,6 +427,7 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
FRIEND_TEST(SnapshotTest, MergeFailureCode);
|
||||
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
|
||||
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
|
||||
FRIEND_TEST(SnapshotTest, BootSnapshotWithoutSlotSwitch);
|
||||
FRIEND_TEST(SnapshotUpdateTest, AddPartition);
|
||||
FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
|
||||
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
|
||||
|
@ -436,6 +440,7 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);
|
||||
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
|
||||
FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
|
||||
FRIEND_TEST(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch);
|
||||
friend class SnapshotTest;
|
||||
friend class SnapshotUpdateTest;
|
||||
friend class FlashAfterUpdateTest;
|
||||
|
@ -456,7 +461,7 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
bool EnsureImageManager();
|
||||
|
||||
// Ensure we're connected to snapuserd.
|
||||
bool EnsureSnapuserdConnected();
|
||||
bool EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms = 10s);
|
||||
|
||||
// Helpers for first-stage init.
|
||||
const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
|
||||
|
@ -549,6 +554,16 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
// Unmap and remove all known snapshots.
|
||||
bool RemoveAllSnapshots(LockedFile* lock);
|
||||
|
||||
// Boot device off snapshots without slot switch
|
||||
bool BootFromSnapshotsWithoutSlotSwitch();
|
||||
|
||||
// Remove kBootSnapshotsWithoutSlotSwitch so that device can boot
|
||||
// without snapshots on the current slot
|
||||
bool PrepareDeviceToBootWithoutSnapshot();
|
||||
|
||||
// Is the kBootSnapshotsWithoutSlotSwitch present
|
||||
bool IsSnapshotWithoutSlotSwitch();
|
||||
|
||||
// List the known snapshot names.
|
||||
bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
|
||||
const std::string& suffix = "");
|
||||
|
@ -663,6 +678,7 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
std::string GetRollbackIndicatorPath();
|
||||
std::string GetForwardMergeIndicatorPath();
|
||||
std::string GetOldPartitionMetadataPath();
|
||||
std::string GetBootSnapshotsWithoutSlotSwitchPath();
|
||||
|
||||
const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
|
||||
|
||||
|
|
|
@ -83,6 +83,8 @@ using std::chrono::duration_cast;
|
|||
using namespace std::chrono_literals;
|
||||
using namespace std::string_literals;
|
||||
|
||||
static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
|
||||
"/metadata/ota/snapshot-boot-without-slot-switch";
|
||||
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
|
||||
static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
|
||||
static constexpr auto kUpdateStateCheckInterval = 2s;
|
||||
|
@ -217,6 +219,12 @@ bool SnapshotManager::TryCancelUpdate(bool* needs_merge) {
|
|||
auto file = LockExclusive();
|
||||
if (!file) return false;
|
||||
|
||||
if (IsSnapshotWithoutSlotSwitch()) {
|
||||
LOG(ERROR) << "Cannot cancel the snapshots as partitions are mounted off the snapshots on "
|
||||
"current slot.";
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateState state = ReadUpdateState(file.get());
|
||||
if (state == UpdateState::None) {
|
||||
RemoveInvalidSnapshots(file.get());
|
||||
|
@ -299,10 +307,9 @@ bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function
|
|||
// - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator
|
||||
// matches the incoming update.
|
||||
std::vector<std::string> files = {
|
||||
GetSnapshotBootIndicatorPath(),
|
||||
GetRollbackIndicatorPath(),
|
||||
GetForwardMergeIndicatorPath(),
|
||||
GetOldPartitionMetadataPath(),
|
||||
GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath(),
|
||||
GetForwardMergeIndicatorPath(), GetOldPartitionMetadataPath(),
|
||||
GetBootSnapshotsWithoutSlotSwitchPath(),
|
||||
};
|
||||
for (const auto& file : files) {
|
||||
RemoveFileIfExists(file);
|
||||
|
@ -483,6 +490,32 @@ bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
|
|||
LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
|
||||
return false;
|
||||
}
|
||||
} else if (IsSnapshotWithoutSlotSwitch()) {
|
||||
// When snapshots are on current slot, we determine the size
|
||||
// of block device based on the number of COW operations. We cannot
|
||||
// use base device as it will be from older image.
|
||||
size_t num_ops = 0;
|
||||
uint64_t dev_sz = 0;
|
||||
unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (fd < 0) {
|
||||
PLOG(ERROR) << "Failed to open " << cow_file;
|
||||
return false;
|
||||
}
|
||||
|
||||
CowReader reader;
|
||||
if (!reader.Parse(std::move(fd))) {
|
||||
LOG(ERROR) << "Failed to parse cow " << cow_file;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& header = reader.GetHeader();
|
||||
if (header.prefix.major_version > 2) {
|
||||
LOG(ERROR) << "COW format not supported";
|
||||
return false;
|
||||
}
|
||||
num_ops = reader.get_num_total_data_ops();
|
||||
dev_sz = (num_ops * header.block_size);
|
||||
base_sectors = dev_sz >> 9;
|
||||
} else {
|
||||
// For userspace snapshots, the size of the base device is taken as the
|
||||
// size of the dm-user block device. Since there is no pseudo mapping
|
||||
|
@ -1479,6 +1512,10 @@ MergeFailureCode SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::string SnapshotManager::GetBootSnapshotsWithoutSlotSwitchPath() {
|
||||
return metadata_dir_ + "/" + android::base::Basename(kBootSnapshotsWithoutSlotSwitch);
|
||||
}
|
||||
|
||||
std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
|
||||
return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
|
||||
}
|
||||
|
@ -2120,6 +2157,10 @@ UpdateState SnapshotManager::GetUpdateState(double* progress) {
|
|||
return state;
|
||||
}
|
||||
|
||||
bool SnapshotManager::IsSnapshotWithoutSlotSwitch() {
|
||||
return (access(GetBootSnapshotsWithoutSlotSwitchPath().c_str(), F_OK) == 0);
|
||||
}
|
||||
|
||||
bool SnapshotManager::UpdateUsesCompression() {
|
||||
auto lock = LockShared();
|
||||
if (!lock) return false;
|
||||
|
@ -2212,6 +2253,13 @@ std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
|
|||
}
|
||||
|
||||
bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
|
||||
if (IsSnapshotWithoutSlotSwitch()) {
|
||||
if (GetCurrentSlot() != Slot::Source) {
|
||||
LOG(ERROR) << "Snapshots marked to boot without slot switch; but slot is wrong";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// If we fail to read, we'll wind up using CreateLogicalPartitions, which
|
||||
// will create devices that look like the old slot, except with extra
|
||||
// content at the end of each device. This will confuse dm-verity, and
|
||||
|
@ -2347,7 +2395,8 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
|
|||
// completed, live_snapshot_status is set to nullopt.
|
||||
std::optional<SnapshotStatus> live_snapshot_status;
|
||||
do {
|
||||
if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
|
||||
if (!IsSnapshotWithoutSlotSwitch() &&
|
||||
!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
|
||||
LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
|
||||
<< params.GetPartitionName();
|
||||
break;
|
||||
|
@ -2703,7 +2752,7 @@ bool SnapshotManager::UnmapUserspaceSnapshotDevice(LockedFile* lock,
|
|||
// to unmap; hence, we can't be deleting the device
|
||||
// as the table would be mounted off partitions and will fail.
|
||||
if (snapshot_status.state() != SnapshotState::MERGE_COMPLETED) {
|
||||
if (!DeleteDeviceIfExists(dm_user_name)) {
|
||||
if (!DeleteDeviceIfExists(dm_user_name, 4000ms)) {
|
||||
LOG(ERROR) << "Cannot unmap " << dm_user_name;
|
||||
return false;
|
||||
}
|
||||
|
@ -3098,7 +3147,7 @@ bool SnapshotManager::EnsureImageManager() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SnapshotManager::EnsureSnapuserdConnected() {
|
||||
bool SnapshotManager::EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms) {
|
||||
if (snapuserd_client_) {
|
||||
return true;
|
||||
}
|
||||
|
@ -3107,7 +3156,7 @@ bool SnapshotManager::EnsureSnapuserdConnected() {
|
|||
return false;
|
||||
}
|
||||
|
||||
snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
|
||||
snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, timeout_ms);
|
||||
if (!snapuserd_client_) {
|
||||
LOG(ERROR) << "Unable to connect to snapuserd";
|
||||
return false;
|
||||
|
@ -4372,13 +4421,70 @@ std::string SnapshotManager::ReadSourceBuildFingerprint() {
|
|||
bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
|
||||
auto slot = GetCurrentSlot();
|
||||
if (slot == Slot::Target) {
|
||||
// Merge in-progress
|
||||
if (IsSnapuserdRequired()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Let's check more deeper to see if snapshots are mounted
|
||||
auto lock = LockExclusive();
|
||||
if (!lock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> snapshots;
|
||||
if (!ListSnapshots(lock.get(), &snapshots)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& snapshot : snapshots) {
|
||||
// Active snapshot and daemon is alive
|
||||
if (IsSnapshotDevice(snapshot) && EnsureSnapuserdConnected(2s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapshotManager::BootFromSnapshotsWithoutSlotSwitch() {
|
||||
auto lock = LockExclusive();
|
||||
if (!lock) return false;
|
||||
|
||||
auto contents = device_->GetSlotSuffix();
|
||||
// This is the indicator which tells first-stage init
|
||||
// to boot from snapshots even though there was no slot-switch
|
||||
auto boot_file = GetBootSnapshotsWithoutSlotSwitchPath();
|
||||
if (!WriteStringToFileAtomic(contents, boot_file)) {
|
||||
PLOG(ERROR) << "write failed: " << boot_file;
|
||||
return false;
|
||||
}
|
||||
|
||||
SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
|
||||
update_status.set_state(UpdateState::Initiated);
|
||||
update_status.set_userspace_snapshots(true);
|
||||
update_status.set_using_snapuserd(true);
|
||||
if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SnapshotManager::PrepareDeviceToBootWithoutSnapshot() {
|
||||
auto lock = LockExclusive();
|
||||
if (!lock) return false;
|
||||
|
||||
android::base::RemoveFileIfExists(GetSnapshotBootIndicatorPath());
|
||||
android::base::RemoveFileIfExists(GetBootSnapshotsWithoutSlotSwitchPath());
|
||||
|
||||
SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
|
||||
update_status.set_state(UpdateState::Cancelled);
|
||||
if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
@ -2559,6 +2559,56 @@ TEST_F(SnapshotUpdateTest, DaemonTransition) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch) {
|
||||
MountMetadata();
|
||||
AddOperationForPartitions();
|
||||
// Execute the update.
|
||||
ASSERT_TRUE(sm->BeginUpdate());
|
||||
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
|
||||
|
||||
if (!sm->UpdateUsesUserSnapshots()) {
|
||||
GTEST_SKIP() << "Test does not apply as UserSnapshots aren't enabled.";
|
||||
}
|
||||
|
||||
ASSERT_TRUE(WriteSnapshots());
|
||||
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
|
||||
|
||||
if (ShouldSkipLegacyMerging()) {
|
||||
GTEST_SKIP() << "Skipping legacy merge test";
|
||||
}
|
||||
// Mark the indicator
|
||||
ASSERT_TRUE(sm->BootFromSnapshotsWithoutSlotSwitch());
|
||||
|
||||
ASSERT_TRUE(sm->EnsureSnapuserdConnected());
|
||||
sm->set_use_first_stage_snapuserd(true);
|
||||
|
||||
ASSERT_TRUE(sm->NeedSnapshotsInFirstStageMount());
|
||||
|
||||
// Map snapshots
|
||||
ASSERT_TRUE(sm->MapAllSnapshots(10s));
|
||||
|
||||
// New updates should fail
|
||||
ASSERT_FALSE(sm->BeginUpdate());
|
||||
|
||||
// Snapshots cannot be cancelled
|
||||
ASSERT_FALSE(sm->CancelUpdate());
|
||||
|
||||
// Merge cannot start
|
||||
ASSERT_FALSE(sm->InitiateMerge());
|
||||
|
||||
// Read bytes back and verify they match the cache.
|
||||
ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
|
||||
|
||||
// Remove the indicators
|
||||
ASSERT_TRUE(sm->PrepareDeviceToBootWithoutSnapshot());
|
||||
|
||||
// Ensure snapshots are still mounted
|
||||
ASSERT_TRUE(sm->IsUserspaceSnapshotUpdateInProgress());
|
||||
|
||||
// Cleanup snapshots
|
||||
ASSERT_TRUE(sm->UnmapAllSnapshots());
|
||||
}
|
||||
|
||||
TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
|
||||
AddOperationForPartitions();
|
||||
// Execute the update.
|
||||
|
|
|
@ -75,7 +75,11 @@ int Usage() {
|
|||
" unmap-snapshots\n"
|
||||
" Unmap all pre-created snapshots\n"
|
||||
" delete-snapshots\n"
|
||||
" Delete all pre-created snapshots\n";
|
||||
" Delete all pre-created snapshots\n"
|
||||
" revert-snapshots\n"
|
||||
" Prepares devices to boot without snapshots on next boot.\n"
|
||||
" This does not delete the snapshot. It only removes the indicators\n"
|
||||
" so that first stage init will not mount from snapshots.\n";
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
|
@ -87,9 +91,11 @@ class MapSnapshots {
|
|||
MapSnapshots(std::string path = "");
|
||||
bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
|
||||
bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
|
||||
bool WaitForSnapshotWritesToComplete();
|
||||
bool FinishSnapshotWrites();
|
||||
bool UnmapCowImagePath(std::string& name);
|
||||
bool DeleteCowImage(std::string& name);
|
||||
bool DeleteSnapshots();
|
||||
bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
|
||||
bool BeginUpdate();
|
||||
|
||||
private:
|
||||
std::optional<std::string> GetCowImagePath(std::string& name);
|
||||
|
@ -107,7 +113,24 @@ MapSnapshots::MapSnapshots(std::string path) {
|
|||
exit(1);
|
||||
}
|
||||
snapshot_dir_path_ = path + "/";
|
||||
}
|
||||
|
||||
bool MapSnapshots::BeginUpdate() {
|
||||
lock_ = sm_->LockExclusive();
|
||||
std::vector<std::string> snapshots;
|
||||
sm_->ListSnapshots(lock_.get(), &snapshots);
|
||||
if (!snapshots.empty()) {
|
||||
// Snapshots are already present.
|
||||
return true;
|
||||
}
|
||||
|
||||
lock_ = nullptr;
|
||||
if (!sm_->BeginUpdate()) {
|
||||
LOG(ERROR) << "BeginUpdate failed";
|
||||
return false;
|
||||
}
|
||||
lock_ = sm_->LockExclusive();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {
|
||||
|
@ -130,6 +153,9 @@ bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string
|
|||
dev_sz &= ~(block_sz - 1);
|
||||
|
||||
SnapshotStatus status;
|
||||
status.set_state(SnapshotState::CREATED);
|
||||
status.set_using_snapuserd(true);
|
||||
status.set_old_partition_size(0);
|
||||
status.set_name(partition_name);
|
||||
status.set_cow_file_size(dev_sz);
|
||||
status.set_cow_partition_size(0);
|
||||
|
@ -216,27 +242,33 @@ bool MapSnapshots::InitiateThreadedSnapshotWrite(std::string& pname, std::string
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MapSnapshots::WaitForSnapshotWritesToComplete() {
|
||||
bool MapSnapshots::FinishSnapshotWrites() {
|
||||
bool ret = true;
|
||||
for (auto& t : threads_) {
|
||||
ret = t.get() && ret;
|
||||
}
|
||||
|
||||
lock_ = nullptr;
|
||||
if (ret) {
|
||||
LOG(INFO) << "Pre-created snapshots successfully copied";
|
||||
} else {
|
||||
LOG(ERROR) << "Snapshot copy failed";
|
||||
if (!sm_->FinishedSnapshotWrites(false)) {
|
||||
return false;
|
||||
}
|
||||
return sm_->BootFromSnapshotsWithoutSlotSwitch();
|
||||
}
|
||||
return ret;
|
||||
|
||||
LOG(ERROR) << "Snapshot copy failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MapSnapshots::UnmapCowImagePath(std::string& name) {
|
||||
return sm_->UnmapCowImage(name);
|
||||
}
|
||||
|
||||
bool MapSnapshots::DeleteCowImage(std::string& name) {
|
||||
if (!sm_->DeleteSnapshot(lock_.get(), name)) {
|
||||
LOG(ERROR) << "Delete snapshot failed";
|
||||
bool MapSnapshots::DeleteSnapshots() {
|
||||
lock_ = sm_->LockExclusive();
|
||||
if (!sm_->RemoveAllUpdateState(lock_.get())) {
|
||||
LOG(ERROR) << "Remove All Update State failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -281,7 +313,8 @@ bool GetVerityPartitions(std::vector<std::string>& partitions) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool UnMapPrecreatedSnapshots(int, char**) {
|
||||
bool UnMapPrecreatedSnapshots(int, char** argv) {
|
||||
android::base::InitLogging(argv, &android::base::KernelLogger);
|
||||
// Make sure we are root.
|
||||
if (::getuid() != 0) {
|
||||
LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
|
||||
|
@ -302,29 +335,36 @@ bool UnMapPrecreatedSnapshots(int, char**) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DeletePrecreatedSnapshots(int, char**) {
|
||||
bool RemovePrecreatedSnapshots(int, char** argv) {
|
||||
android::base::InitLogging(argv, &android::base::KernelLogger);
|
||||
// Make sure we are root.
|
||||
if (::getuid() != 0) {
|
||||
LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
|
||||
return false;
|
||||
}
|
||||
|
||||
MapSnapshots snapshot;
|
||||
if (!snapshot.CleanupSnapshot()) {
|
||||
LOG(ERROR) << "CleanupSnapshot failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeletePrecreatedSnapshots(int, char** argv) {
|
||||
android::base::InitLogging(argv, &android::base::KernelLogger);
|
||||
// Make sure we are root.
|
||||
if (::getuid() != 0) {
|
||||
LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::vector<std::string> partitions;
|
||||
if (!GetVerityPartitions(partitions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MapSnapshots snapshot;
|
||||
for (auto partition : partitions) {
|
||||
if (!snapshot.DeleteCowImage(partition)) {
|
||||
LOG(ERROR) << "DeleteCowImage failed: " << partition;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return snapshot.DeleteSnapshots();
|
||||
}
|
||||
|
||||
bool MapPrecreatedSnapshots(int argc, char** argv) {
|
||||
android::base::InitLogging(argv, &android::base::StderrLogger);
|
||||
android::base::InitLogging(argv, &android::base::KernelLogger);
|
||||
|
||||
// Make sure we are root.
|
||||
if (::getuid() != 0) {
|
||||
|
@ -365,6 +405,11 @@ bool MapPrecreatedSnapshots(int argc, char** argv) {
|
|||
}
|
||||
|
||||
MapSnapshots cow(path);
|
||||
if (!cow.BeginUpdate()) {
|
||||
LOG(ERROR) << "BeginUpdate failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& pair : partitions) {
|
||||
if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {
|
||||
LOG(ERROR) << "CreateSnapshotDevice failed for: " << pair.first;
|
||||
|
@ -376,7 +421,7 @@ bool MapPrecreatedSnapshots(int argc, char** argv) {
|
|||
}
|
||||
}
|
||||
|
||||
return cow.WaitForSnapshotWritesToComplete();
|
||||
return cow.FinishSnapshotWrites();
|
||||
}
|
||||
|
||||
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
|
||||
|
@ -508,6 +553,7 @@ static std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {
|
|||
{"map-snapshots", MapPrecreatedSnapshots},
|
||||
{"unmap-snapshots", UnMapPrecreatedSnapshots},
|
||||
{"delete-snapshots", DeletePrecreatedSnapshots},
|
||||
{"revert-snapshots", RemovePrecreatedSnapshots},
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue