1 //
2 // Copyright (C) 2019 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <sysexits.h>
18 #include <unistd.h>
19 
20 #include <chrono>
21 #include <filesystem>
22 #include <fstream>
23 #include <future>
24 #include <iostream>
25 #include <map>
26 #include <sstream>
27 #include <thread>
28 
29 #include <android-base/file.h>
30 #include <android-base/logging.h>
31 #include <android-base/unique_fd.h>
32 
33 #include <android-base/chrono_utils.h>
34 #include <android-base/parseint.h>
35 #include <android-base/properties.h>
36 #include <android-base/scopeguard.h>
37 #include <android-base/stringprintf.h>
38 #include <android-base/strings.h>
39 
40 #include <fs_mgr.h>
41 #include <fs_mgr_dm_linear.h>
42 #include <fstab/fstab.h>
43 #include <liblp/builder.h>
44 #include <libsnapshot/cow_format.h>
45 #include <libsnapshot/snapshot.h>
46 #include <storage_literals/storage_literals.h>
47 
48 #include "partition_cow_creator.h"
49 
50 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
51 #include <BootControlClient.h>
52 #endif
53 
54 using namespace std::chrono_literals;
55 using namespace std::string_literals;
56 using namespace android::storage_literals;
57 using android::base::LogdLogger;
58 using android::base::StderrLogger;
59 using android::base::TeeLogger;
60 using android::fs_mgr::CreateLogicalPartitionParams;
61 using android::fs_mgr::FindPartition;
62 using android::fs_mgr::GetPartitionSize;
63 using android::fs_mgr::PartitionOpener;
64 using android::fs_mgr::ReadMetadata;
65 using android::fs_mgr::SlotNumberForSlotSuffix;
66 
Usage()67 int Usage() {
68     std::cerr << "snapshotctl: Control snapshots.\n"
69                  "Usage: snapshotctl [action] [flags]\n"
70                  "Actions:\n"
71                  "  dump\n"
72                  "    Print snapshot states.\n"
73                  "  merge\n"
74                  "    Deprecated.\n"
75                  "  map\n"
76                  "    Map all partitions at /dev/block/mapper\n"
77                  "  map-snapshots <directory where snapshot patches are present>\n"
78                  "    Map all snapshots based on patches present in the directory\n"
79                  "  unmap-snapshots\n"
80                  "    Unmap all pre-created snapshots\n"
81                  "  delete-snapshots\n"
82                  "    Delete all pre-created snapshots\n"
83                  "  revert-snapshots\n"
84                  "    Prepares devices to boot without snapshots on next boot.\n"
85                  "    This does not delete the snapshot. It only removes the indicators\n"
86                  "    so that first stage init will not mount from snapshots.\n"
87                  "  apply-update\n"
88                  "    Apply the incremental OTA update wherein the snapshots are\n"
89                  "    directly written to COW block device. This will bypass update-engine\n"
90                  "    and the device will be ready to boot from the target build.\n";
91     return EX_USAGE;
92 }
93 
94 namespace android {
95 namespace snapshot {
96 
97 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
98 class MapSnapshots {
99   public:
100     MapSnapshots(std::string path = "");
101     bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
102     bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
103     bool FinishSnapshotWrites();
104     bool UnmapCowImagePath(std::string& name);
105     bool DeleteSnapshots();
CleanupSnapshot()106     bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
107     bool BeginUpdate();
108     bool ApplyUpdate();
109 
110   private:
111     std::optional<std::string> GetCowImagePath(std::string& name);
112     bool PrepareUpdate();
113     bool GetCowDevicePath(std::string partition_name, std::string* cow_path);
114     bool WriteSnapshotPatch(std::string cow_device, std::string patch);
115     std::string GetGroupName(const android::fs_mgr::LpMetadata& pt,
116                              const std::string& partiton_name);
117     std::unique_ptr<SnapshotManager::LockedFile> lock_;
118     std::unique_ptr<SnapshotManager> sm_;
119     std::vector<std::future<bool>> threads_;
120     std::string snapshot_dir_path_;
121     std::unordered_map<std::string, chromeos_update_engine::DynamicPartitionGroup*> group_map_;
122 
123     std::vector<std::string> patchfiles_;
124     chromeos_update_engine::DeltaArchiveManifest manifest_;
125 };
126 
MapSnapshots(std::string path)127 MapSnapshots::MapSnapshots(std::string path) {
128     sm_ = SnapshotManager::New();
129     if (!sm_) {
130         std::cout << "Failed to create snapshotmanager";
131         exit(1);
132     }
133     snapshot_dir_path_ = path + "/";
134 }
135 
GetGroupName(const android::fs_mgr::LpMetadata & pt,const std::string & partition_name)136 std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
137                                        const std::string& partition_name) {
138     std::string group_name;
139     for (const auto& partition : pt.partitions) {
140         std::string name = android::fs_mgr::GetPartitionName(partition);
141         auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
142         std::string pname = name.substr(0, name.size() - suffix.size());
143         if (pname == partition_name) {
144             std::string group_name =
145                     android::fs_mgr::GetPartitionGroupName(pt.groups[partition.group_index]);
146             return group_name.substr(0, group_name.size() - suffix.size());
147         }
148     }
149     return "";
150 }
151 
PrepareUpdate()152 bool MapSnapshots::PrepareUpdate() {
153     auto source_slot = fs_mgr_get_slot_suffix();
154     auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
155     auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
156 
157     // Get current partition information.
158     PartitionOpener opener;
159     auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);
160     if (!source_metadata) {
161         LOG(ERROR) << "Could not read source partition metadata.\n";
162         return false;
163     }
164 
165     auto dap = manifest_.mutable_dynamic_partition_metadata();
166     dap->set_snapshot_enabled(true);
167     dap->set_vabc_enabled(true);
168     dap->set_vabc_compression_param("lz4");
169     dap->set_cow_version(3);
170 
171     for (const auto& entry : std::filesystem::directory_iterator(snapshot_dir_path_)) {
172         if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
173             patchfiles_.push_back(android::base::Basename(entry.path().generic_string()));
174         }
175     }
176 
177     for (auto& patchfile : patchfiles_) {
178         std::string parsing_file = snapshot_dir_path_ + patchfile;
179         android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
180         if (fd < 0) {
181             LOG(ERROR) << "Failed to open file: " << parsing_file;
182             return false;
183         }
184         uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
185         if (!dev_sz) {
186             LOG(ERROR) << "Could not determine block device size: " << parsing_file;
187             return false;
188         }
189 
190         const int block_sz = 4_KiB;
191         dev_sz += block_sz - 1;
192         dev_sz &= ~(block_sz - 1);
193 
194         auto npos = patchfile.rfind(".patch");
195         auto partition_name = patchfile.substr(0, npos);
196 
197         chromeos_update_engine::DynamicPartitionGroup* group = nullptr;
198         std::string group_name = GetGroupName(*source_metadata.get(), partition_name);
199         if (group_map_.find(group_name) != group_map_.end()) {
200             group = group_map_[group_name];
201         } else {
202             group = dap->add_groups();
203             group->set_name(group_name);
204             group_map_[group_name] = group;
205         }
206         group->add_partition_names(partition_name);
207 
208         auto pu = manifest_.mutable_partitions()->Add();
209         pu->set_partition_name(partition_name);
210         pu->set_estimate_cow_size(dev_sz);
211 
212         CowReader reader;
213         if (!reader.Parse(fd)) {
214             LOG(ERROR) << "COW reader parse failed";
215             return false;
216         }
217 
218         uint64_t new_device_size = 0;
219         const auto& header = reader.GetHeader();
220         if (header.prefix.major_version == 2) {
221             size_t num_ops = reader.get_num_total_data_ops();
222             new_device_size = (num_ops * header.block_size);
223         } else {
224             const auto& v3_header = reader.header_v3();
225             new_device_size = v3_header.op_count_max * v3_header.block_size;
226         }
227 
228         LOG(INFO) << "Partition: " << partition_name << " Group_name: " << group_name
229                   << " size: " << new_device_size << " COW-size: " << dev_sz;
230         pu->mutable_new_partition_info()->set_size(new_device_size);
231     }
232     return true;
233 }
234 
GetCowDevicePath(std::string partition_name,std::string * cow_path)235 bool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {
236     auto& dm = android::dm::DeviceMapper::Instance();
237     std::string cow_device = partition_name + "-cow";
238     if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
239         return true;
240     }
241 
242     LOG(INFO) << "Failed to find cow path: " << cow_device << " Checking the device for -img path";
243     // If the COW device exists only on /data
244     cow_device = partition_name + "-cow-img";
245     if (!dm.GetDmDevicePathByName(cow_device, cow_path)) {
246         LOG(ERROR) << "Failed to cow path: " << cow_device;
247         return false;
248     }
249     return true;
250 }
251 
ApplyUpdate()252 bool MapSnapshots::ApplyUpdate() {
253     if (!PrepareUpdate()) {
254         LOG(ERROR) << "PrepareUpdate failed";
255         return false;
256     }
257     if (!sm_->BeginUpdate()) {
258         LOG(ERROR) << "BeginUpdate failed";
259         return false;
260     }
261     if (!sm_->CreateUpdateSnapshots(manifest_)) {
262         LOG(ERROR) << "Could not apply snapshots";
263         return false;
264     }
265 
266     LOG(INFO) << "CreateUpdateSnapshots success";
267     if (!sm_->MapAllSnapshots(10s)) {
268         LOG(ERROR) << "MapAllSnapshots failed";
269         return false;
270     }
271 
272     LOG(INFO) << "MapAllSnapshots success";
273 
274     auto target_slot = fs_mgr_get_other_slot_suffix();
275     for (auto& patchfile : patchfiles_) {
276         auto npos = patchfile.rfind(".patch");
277         auto partition_name = patchfile.substr(0, npos) + target_slot;
278         std::string cow_path;
279         if (!GetCowDevicePath(partition_name, &cow_path)) {
280             LOG(ERROR) << "Failed to find cow path";
281             return false;
282         }
283         threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch,
284                                          this, cow_path, patchfile));
285     }
286 
287     bool ret = true;
288     for (auto& t : threads_) {
289         ret = t.get() && ret;
290     }
291     if (!ret) {
292         LOG(ERROR) << "Snapshot writes failed";
293         return false;
294     }
295     if (!sm_->UnmapAllSnapshots()) {
296         LOG(ERROR) << "UnmapAllSnapshots failed";
297         return false;
298     }
299 
300     LOG(INFO) << "Pre-created snapshots successfully copied";
301     // All snapshots have been written.
302     if (!sm_->FinishedSnapshotWrites(false /* wipe */)) {
303         LOG(ERROR) << "Could not finalize snapshot writes.\n";
304         return false;
305     }
306 
307     auto hal = hal::BootControlClient::WaitForService();
308     if (!hal) {
309         LOG(ERROR) << "Could not find IBootControl HAL.\n";
310         return false;
311     }
312     auto target_slot_number = SlotNumberForSlotSuffix(target_slot);
313     auto cr = hal->SetActiveBootSlot(target_slot_number);
314     if (!cr.IsOk()) {
315         LOG(ERROR) << "Could not set active boot slot: " << cr.errMsg;
316         return false;
317     }
318 
319     LOG(INFO) << "ApplyUpdate success";
320     return true;
321 }
322 
BeginUpdate()323 bool MapSnapshots::BeginUpdate() {
324     lock_ = sm_->LockExclusive();
325     std::vector<std::string> snapshots;
326     sm_->ListSnapshots(lock_.get(), &snapshots);
327     if (!snapshots.empty()) {
328         // Snapshots are already present.
329         return true;
330     }
331 
332     lock_ = nullptr;
333     if (!sm_->BeginUpdate()) {
334         LOG(ERROR) << "BeginUpdate failed";
335         return false;
336     }
337     lock_ = sm_->LockExclusive();
338     return true;
339 }
340 
CreateSnapshotDevice(std::string & partition_name,std::string & patchfile)341 bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {
342     std::string parsing_file = snapshot_dir_path_ + patchfile;
343 
344     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
345     if (fd < 0) {
346         LOG(ERROR) << "Failed to open file: " << parsing_file;
347         return false;
348     }
349 
350     uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
351     if (!dev_sz) {
352         LOG(ERROR) << "Could not determine block device size: " << parsing_file;
353         return false;
354     }
355 
356     const int block_sz = 4_KiB;
357     dev_sz += block_sz - 1;
358     dev_sz &= ~(block_sz - 1);
359 
360     SnapshotStatus status;
361     status.set_state(SnapshotState::CREATED);
362     status.set_using_snapuserd(true);
363     status.set_old_partition_size(0);
364     status.set_name(partition_name);
365     status.set_cow_file_size(dev_sz);
366     status.set_cow_partition_size(0);
367 
368     PartitionCowCreator cow_creator;
369     cow_creator.using_snapuserd = true;
370 
371     if (!sm_->CreateSnapshot(lock_.get(), &cow_creator, &status)) {
372         LOG(ERROR) << "CreateSnapshot failed";
373         return false;
374     }
375 
376     if (!sm_->CreateCowImage(lock_.get(), partition_name)) {
377         LOG(ERROR) << "CreateCowImage failed";
378         return false;
379     }
380 
381     return true;
382 }
383 
GetCowImagePath(std::string & name)384 std::optional<std::string> MapSnapshots::GetCowImagePath(std::string& name) {
385     auto cow_dev = sm_->MapCowImage(name, 5s);
386     if (!cow_dev.has_value()) {
387         LOG(ERROR) << "Failed to get COW device path";
388         return std::nullopt;
389     }
390 
391     LOG(INFO) << "COW Device path: " << cow_dev.value();
392     return cow_dev;
393 }
394 
WriteSnapshotPatch(std::string cow_device,std::string patch)395 bool MapSnapshots::WriteSnapshotPatch(std::string cow_device, std::string patch) {
396     std::string patch_file = snapshot_dir_path_ + patch;
397 
398     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(patch_file.c_str(), O_RDONLY)));
399     if (fd < 0) {
400         LOG(ERROR) << "Failed to open file: " << patch_file;
401         return false;
402     }
403 
404     uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
405     if (!dev_sz) {
406         std::cout << "Could not determine block device size: " << patch_file;
407         return false;
408     }
409 
410     android::base::unique_fd cfd(TEMP_FAILURE_RETRY(open(cow_device.c_str(), O_RDWR)));
411     if (cfd < 0) {
412         LOG(ERROR) << "Failed to open file: " << cow_device;
413         return false;
414     }
415 
416     const uint64_t read_sz = 1_MiB;
417     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
418     off_t file_offset = 0;
419 
420     while (true) {
421         size_t to_read = std::min((dev_sz - file_offset), read_sz);
422         if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
423             PLOG(ERROR) << "ReadFullyAtOffset failed";
424             return false;
425         }
426 
427         if (!android::base::WriteFullyAtOffset(cfd, buffer.get(), to_read, file_offset)) {
428             PLOG(ERROR) << "WriteFullyAtOffset failed";
429             return false;
430         }
431         file_offset += to_read;
432         if (file_offset >= dev_sz) {
433             break;
434         }
435     }
436     if (fsync(cfd.get()) < 0) {
437         PLOG(ERROR) << "Fsync failed";
438         return false;
439     }
440     return true;
441 }
442 
InitiateThreadedSnapshotWrite(std::string & pname,std::string & snapshot_patch)443 bool MapSnapshots::InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch) {
444     auto path = GetCowImagePath(pname);
445     if (!path.has_value()) {
446         return false;
447     }
448     threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch, this,
449                                      path.value(), snapshot_patch));
450     return true;
451 }
452 
FinishSnapshotWrites()453 bool MapSnapshots::FinishSnapshotWrites() {
454     bool ret = true;
455     for (auto& t : threads_) {
456         ret = t.get() && ret;
457     }
458 
459     lock_ = nullptr;
460     if (ret) {
461         LOG(INFO) << "Pre-created snapshots successfully copied";
462         if (!sm_->FinishedSnapshotWrites(false)) {
463             return false;
464         }
465         return sm_->BootFromSnapshotsWithoutSlotSwitch();
466     }
467 
468     LOG(ERROR) << "Snapshot copy failed";
469     return false;
470 }
471 
UnmapCowImagePath(std::string & name)472 bool MapSnapshots::UnmapCowImagePath(std::string& name) {
473     return sm_->UnmapCowImage(name);
474 }
475 
DeleteSnapshots()476 bool MapSnapshots::DeleteSnapshots() {
477     lock_ = sm_->LockExclusive();
478     if (!sm_->RemoveAllUpdateState(lock_.get())) {
479         LOG(ERROR) << "Remove All Update State failed";
480         return false;
481     }
482     return true;
483 }
484 #endif
485 
DumpCmdHandler(int,char ** argv)486 bool DumpCmdHandler(int /*argc*/, char** argv) {
487     android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
488     return SnapshotManager::New()->Dump(std::cout);
489 }
490 
MapCmdHandler(int,char ** argv)491 bool MapCmdHandler(int, char** argv) {
492     android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
493     using namespace std::chrono_literals;
494     return SnapshotManager::New()->MapAllSnapshots(5000ms);
495 }
496 
UnmapCmdHandler(int,char ** argv)497 bool UnmapCmdHandler(int, char** argv) {
498     android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
499     return SnapshotManager::New()->UnmapAllSnapshots();
500 }
501 
MergeCmdHandler(int,char ** argv)502 bool MergeCmdHandler(int /*argc*/, char** argv) {
503     android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
504     LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
505     return false;
506 }
507 
508 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
GetVerityPartitions(std::vector<std::string> & partitions)509 bool GetVerityPartitions(std::vector<std::string>& partitions) {
510     auto& dm = android::dm::DeviceMapper::Instance();
511     auto dm_block_devices = dm.FindDmPartitions();
512     if (dm_block_devices.empty()) {
513         LOG(ERROR) << "No dm-enabled block device is found.";
514         return false;
515     }
516 
517     for (auto& block_device : dm_block_devices) {
518         std::string dm_block_name = block_device.first;
519         std::string slot_suffix = fs_mgr_get_slot_suffix();
520         std::string partition = dm_block_name + slot_suffix;
521         partitions.push_back(partition);
522     }
523     return true;
524 }
525 
UnMapPrecreatedSnapshots(int,char ** argv)526 bool UnMapPrecreatedSnapshots(int, char** argv) {
527     android::base::InitLogging(argv, &android::base::KernelLogger);
528     // Make sure we are root.
529     if (::getuid() != 0) {
530         LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
531         return EXIT_FAILURE;
532     }
533 
534     std::vector<std::string> partitions;
535     if (!GetVerityPartitions(partitions)) {
536         return false;
537     }
538 
539     MapSnapshots snapshot;
540     for (auto partition : partitions) {
541         if (!snapshot.UnmapCowImagePath(partition)) {
542             LOG(ERROR) << "UnmapCowImagePath failed: " << partition;
543         }
544     }
545     return true;
546 }
547 
RemovePrecreatedSnapshots(int,char ** argv)548 bool RemovePrecreatedSnapshots(int, char** argv) {
549     android::base::InitLogging(argv, &android::base::KernelLogger);
550     // Make sure we are root.
551     if (::getuid() != 0) {
552         LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
553         return false;
554     }
555 
556     MapSnapshots snapshot;
557     if (!snapshot.CleanupSnapshot()) {
558         LOG(ERROR) << "CleanupSnapshot failed";
559         return false;
560     }
561     return true;
562 }
563 
DeletePrecreatedSnapshots(int,char ** argv)564 bool DeletePrecreatedSnapshots(int, char** argv) {
565     android::base::InitLogging(argv, &android::base::KernelLogger);
566     // Make sure we are root.
567     if (::getuid() != 0) {
568         LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
569         return EXIT_FAILURE;
570     }
571 
572     MapSnapshots snapshot;
573     return snapshot.DeleteSnapshots();
574 }
575 
ApplyUpdate(int argc,char ** argv)576 bool ApplyUpdate(int argc, char** argv) {
577     android::base::InitLogging(argv, &android::base::KernelLogger);
578 
579     // Make sure we are root.
580     if (::getuid() != 0) {
581         LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
582         return EXIT_FAILURE;
583     }
584 
585     if (argc < 3) {
586         std::cerr << " apply-update <directory location where snapshot patches are present>"
587                      "    Apply the snapshots to the COW block device\n";
588         return false;
589     }
590 
591     std::string path = std::string(argv[2]);
592     MapSnapshots cow(path);
593     if (!cow.ApplyUpdate()) {
594         return false;
595     }
596     LOG(INFO) << "Apply update success. Please reboot the device";
597     return true;
598 }
599 
MapPrecreatedSnapshots(int argc,char ** argv)600 bool MapPrecreatedSnapshots(int argc, char** argv) {
601     android::base::InitLogging(argv, &android::base::KernelLogger);
602 
603     // Make sure we are root.
604     if (::getuid() != 0) {
605         LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
606         return EXIT_FAILURE;
607     }
608 
609     if (argc < 3) {
610         std::cerr << " map-snapshots <directory location where snapshot patches are present>"
611                      "    Map all snapshots based on patches present in the directory\n";
612         return false;
613     }
614 
615     std::string path = std::string(argv[2]);
616     std::vector<std::string> patchfiles;
617 
618     for (const auto& entry : std::filesystem::directory_iterator(path)) {
619         if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
620             patchfiles.push_back(android::base::Basename(entry.path().generic_string()));
621         }
622     }
623     auto& dm = android::dm::DeviceMapper::Instance();
624     auto dm_block_devices = dm.FindDmPartitions();
625     if (dm_block_devices.empty()) {
626         LOG(ERROR) << "No dm-enabled block device is found.";
627         return false;
628     }
629 
630     std::vector<std::pair<std::string, std::string>> partitions;
631     for (auto& patchfile : patchfiles) {
632         auto npos = patchfile.rfind(".patch");
633         auto dm_block_name = patchfile.substr(0, npos);
634         if (dm_block_devices.find(dm_block_name) != dm_block_devices.end()) {
635             std::string slot_suffix = fs_mgr_get_slot_suffix();
636             std::string partition = dm_block_name + slot_suffix;
637             partitions.push_back(std::make_pair(partition, patchfile));
638         }
639     }
640 
641     MapSnapshots cow(path);
642     if (!cow.BeginUpdate()) {
643         LOG(ERROR) << "BeginUpdate failed";
644         return false;
645     }
646 
647     for (auto& pair : partitions) {
648         if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {
649             LOG(ERROR) << "CreateSnapshotDevice failed for: " << pair.first;
650             return false;
651         }
652         if (!cow.InitiateThreadedSnapshotWrite(pair.first, pair.second)) {
653             LOG(ERROR) << "InitiateThreadedSnapshotWrite failed for: " << pair.first;
654             return false;
655         }
656     }
657 
658     return cow.FinishSnapshotWrites();
659 }
660 
CreateTestUpdate(SnapshotManager * sm)661 bool CreateTestUpdate(SnapshotManager* sm) {
662     chromeos_update_engine::DeltaArchiveManifest manifest;
663 
664     // We only copy system, to simplify things.
665     manifest.set_partial_update(true);
666 
667     auto dap = manifest.mutable_dynamic_partition_metadata();
668     dap->set_snapshot_enabled(true);
669     dap->set_vabc_enabled(true);
670     dap->set_vabc_compression_param("none");
671     dap->set_cow_version(kCowVersionMajor);
672 
673     auto source_slot = fs_mgr_get_slot_suffix();
674     auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
675     auto target_slot = fs_mgr_get_other_slot_suffix();
676     auto target_slot_number = SlotNumberForSlotSuffix(target_slot);
677     auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
678 
679     // Get current partition information.
680     PartitionOpener opener;
681     auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);
682     if (!source_metadata) {
683         std::cerr << "Could not read source partition metadata.\n";
684         return false;
685     }
686 
687     auto system_source_name = "system" + source_slot;
688     auto system_source = FindPartition(*source_metadata.get(), system_source_name);
689     if (!system_source) {
690         std::cerr << "Could not find system partition: " << system_source_name << ".\n";
691         return false;
692     }
693     auto system_source_size = GetPartitionSize(*source_metadata.get(), *system_source);
694 
695     // Since we only add copy operations, 64MB should be enough.
696     auto system_update = manifest.mutable_partitions()->Add();
697     system_update->set_partition_name("system");
698     system_update->set_estimate_cow_size(64_MiB);
699     system_update->mutable_new_partition_info()->set_size(system_source_size);
700 
701     if (!sm->CreateUpdateSnapshots(manifest)) {
702         std::cerr << "Could not create update snapshots.\n";
703         return false;
704     }
705 
706     // Write the "new" system partition.
707     auto system_target_name = "system" + target_slot;
708     CreateLogicalPartitionParams clpp = {
709             .block_device = fs_mgr_get_super_partition_name(target_slot_number),
710             .metadata_slot = {target_slot_number},
711             .partition_name = system_target_name,
712             .timeout_ms = 10s,
713             .partition_opener = &opener,
714     };
715     auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);
716     if (!writer) {
717         std::cerr << "Could not open snapshot writer.\n";
718         return false;
719     }
720 
721     for (uint64_t block = 0; block < system_source_size / 4096; block++) {
722         if (!writer->AddCopy(block, block)) {
723             std::cerr << "Unable to add copy operation for block " << block << ".\n";
724             return false;
725         }
726     }
727     if (!writer->Finalize()) {
728         std::cerr << "Could not finalize COW for " << system_target_name << ".\n";
729         return false;
730     }
731     writer = nullptr;
732 
733     // Finished writing this partition, unmap.
734     if (!sm->UnmapUpdateSnapshot(system_target_name)) {
735         std::cerr << "Could not unmap snapshot for " << system_target_name << ".\n";
736         return false;
737     }
738 
739     // All snapshots have been written.
740     if (!sm->FinishedSnapshotWrites(false /* wipe */)) {
741         std::cerr << "Could not finalize snapshot writes.\n";
742         return false;
743     }
744 
745     auto hal = hal::BootControlClient::WaitForService();
746     if (!hal) {
747         std::cerr << "Could not find IBootControl HAL.\n";
748         return false;
749     }
750     auto cr = hal->SetActiveBootSlot(target_slot_number);
751     if (!cr.IsOk()) {
752         std::cerr << "Could not set active boot slot: " << cr.errMsg;
753         return false;
754     }
755 
756     std::cerr << "It is now safe to reboot your device. If using a physical device, make\n"
757               << "sure that all physical partitions are flashed to both A and B slots.\n";
758     return true;
759 }
760 
TestOtaHandler(int,char **)761 bool TestOtaHandler(int /* argc */, char** /* argv */) {
762     auto sm = SnapshotManager::New();
763 
764     if (!sm->BeginUpdate()) {
765         std::cerr << "Error starting update.\n";
766         return false;
767     }
768 
769     if (!CreateTestUpdate(sm.get())) {
770         sm->CancelUpdate();
771         return false;
772     }
773     return true;
774 }
775 #endif
776 
777 static std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {
778         // clang-format off
779         {"dump", DumpCmdHandler},
780         {"merge", MergeCmdHandler},
781         {"map", MapCmdHandler},
782 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
783         {"test-blank-ota", TestOtaHandler},
784         {"apply-update", ApplyUpdate},
785         {"map-snapshots", MapPrecreatedSnapshots},
786         {"unmap-snapshots", UnMapPrecreatedSnapshots},
787         {"delete-snapshots", DeletePrecreatedSnapshots},
788         {"revert-snapshots", RemovePrecreatedSnapshots},
789 #endif
790         {"unmap", UnmapCmdHandler},
791         // clang-format on
792 };
793 
794 }  // namespace snapshot
795 }  // namespace android
796 
main(int argc,char ** argv)797 int main(int argc, char** argv) {
798     using namespace android::snapshot;
799     if (argc < 2) {
800         return Usage();
801     }
802 
803     for (const auto& cmd : kCmdMap) {
804         if (cmd.first == argv[1]) {
805             return cmd.second(argc, argv) ? EX_OK : EX_SOFTWARE;
806         }
807     }
808 
809     return Usage();
810 }
811