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 #include <chrono>
20 #include <filesystem>
21 #include <fstream>
22 #include <future>
23 #include <iostream>
24 #include <map>
25 #include <sstream>
26 #include <thread>
27
28 #include <android-base/file.h>
29 #include <android-base/logging.h>
30 #include <android-base/unique_fd.h>
31
32 #include <android-base/chrono_utils.h>
33 #include <android-base/hex.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 #include <android/snapshot/snapshot.pb.h>
40
41 #include <fs_avb/fs_avb_util.h>
42 #include <fs_mgr.h>
43 #include <fs_mgr_dm_linear.h>
44 #include <fstab/fstab.h>
45 #include <liblp/builder.h>
46 #include <libsnapshot/cow_format.h>
47 #include <libsnapshot/snapshot.h>
48 #include <storage_literals/storage_literals.h>
49
50 #include <openssl/sha.h>
51
52 #include "partition_cow_creator.h"
53 #include "scratch_super.h"
54
55 #include "utility.h"
56
57 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
58 #include <BootControlClient.h>
59 #endif
60
61 using namespace std::chrono_literals;
62 using namespace std::string_literals;
63 using namespace android::storage_literals;
64 using android::base::LogdLogger;
65 using android::base::StderrLogger;
66 using android::base::TeeLogger;
67 using namespace android::dm;
68 using namespace android::fs_mgr;
69 using android::fs_mgr::CreateLogicalPartitionParams;
70 using android::fs_mgr::FindPartition;
71 using android::fs_mgr::GetPartitionSize;
72 using android::fs_mgr::PartitionOpener;
73 using android::fs_mgr::ReadMetadata;
74 using android::fs_mgr::SlotNumberForSlotSuffix;
75
Usage()76 int Usage() {
77 std::cerr << "snapshotctl: Control snapshots.\n"
78 "Usage: snapshotctl [action] [flags]\n"
79 "Actions:\n"
80 " dump\n"
81 " Print snapshot states.\n"
82 " merge\n"
83 " Deprecated.\n"
84 " map\n"
85 " Map all partitions at /dev/block/mapper\n"
86 " pause-merge\n"
87 " Pause snapshot merge\n"
88 " resume-merge\n"
89 " Resume snapshot merge\n"
90 " map-snapshots <directory where snapshot patches are present>\n"
91 " Map all snapshots based on patches present in the directory\n"
92 " unmap-snapshots\n"
93 " Unmap all pre-created snapshots\n"
94 " delete-snapshots\n"
95 " Delete all pre-created snapshots\n"
96 " revert-snapshots\n"
97 " Prepares devices to boot without snapshots on next boot.\n"
98 " This does not delete the snapshot. It only removes the indicators\n"
99 " so that first stage init will not mount from snapshots.\n"
100 " apply-update\n"
101 " Apply the incremental OTA update wherein the snapshots are\n"
102 " directly written to COW block device. This will bypass update-engine\n"
103 " and the device will be ready to boot from the target build.\n"
104 " dump-verity-hash <directory where verity merkel tree hashes are stored> "
105 "[-verify]\n"
106 " Dump the verity merkel tree hashes at the specified path\n"
107 " -verify: Verify the dynamic partition blocks by comparing it with verity "
108 "merkel tree\n";
109 return EX_USAGE;
110 }
111
112 namespace android {
113 namespace snapshot {
114
115 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
116 class MapSnapshots {
117 public:
118 MapSnapshots(std::string path = "", bool metadata_super = false);
119 bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
120 bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
121 bool FinishSnapshotWrites();
122 bool UnmapCowImagePath(std::string& name);
123 bool DeleteSnapshots();
124 bool CleanupSnapshot();
125 bool BeginUpdate();
126 bool ApplyUpdate();
127
128 private:
129 std::optional<std::string> GetCowImagePath(std::string& name);
130 bool PrepareUpdate();
131 bool GetCowDevicePath(std::string partition_name, std::string* cow_path);
132 bool WriteSnapshotPatch(std::string cow_device, std::string patch);
133 std::string GetGroupName(const android::fs_mgr::LpMetadata& pt,
134 const std::string& partiton_name);
135 std::unique_ptr<SnapshotManager::LockedFile> lock_;
136 std::unique_ptr<SnapshotManager> sm_;
137 std::vector<std::future<bool>> threads_;
138 std::string snapshot_dir_path_;
139 std::unordered_map<std::string, chromeos_update_engine::DynamicPartitionGroup*> group_map_;
140
141 std::vector<std::string> patchfiles_;
142 chromeos_update_engine::DeltaArchiveManifest manifest_;
143 bool metadata_super_ = false;
144 };
145
MapSnapshots(std::string path,bool metadata_super)146 MapSnapshots::MapSnapshots(std::string path, bool metadata_super) {
147 snapshot_dir_path_ = path + "/";
148 metadata_super_ = metadata_super;
149 }
150
GetGroupName(const android::fs_mgr::LpMetadata & pt,const std::string & partition_name)151 std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
152 const std::string& partition_name) {
153 std::string group_name;
154 for (const auto& partition : pt.partitions) {
155 std::string name = android::fs_mgr::GetPartitionName(partition);
156 auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
157 std::string pname = name.substr(0, name.size() - suffix.size());
158 if (pname == partition_name) {
159 std::string group_name =
160 android::fs_mgr::GetPartitionGroupName(pt.groups[partition.group_index]);
161 return group_name.substr(0, group_name.size() - suffix.size());
162 }
163 }
164 return "";
165 }
166
PrepareUpdate()167 bool MapSnapshots::PrepareUpdate() {
168 if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
169 LOG(ERROR) << "Failed to create OTA metadata on super";
170 return false;
171 }
172 sm_ = SnapshotManager::New();
173
174 auto source_slot = fs_mgr_get_slot_suffix();
175 auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
176 auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
177
178 // Get current partition information.
179 PartitionOpener opener;
180 auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);
181 if (!source_metadata) {
182 LOG(ERROR) << "Could not read source partition metadata.\n";
183 return false;
184 }
185
186 auto dap = manifest_.mutable_dynamic_partition_metadata();
187 dap->set_snapshot_enabled(true);
188 dap->set_vabc_enabled(true);
189 dap->set_vabc_compression_param("lz4");
190 dap->set_cow_version(3);
191
192 for (const auto& entry : std::filesystem::directory_iterator(snapshot_dir_path_)) {
193 if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
194 patchfiles_.push_back(android::base::Basename(entry.path().generic_string()));
195 }
196 }
197
198 for (auto& patchfile : patchfiles_) {
199 std::string parsing_file = snapshot_dir_path_ + patchfile;
200 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
201 if (fd < 0) {
202 LOG(ERROR) << "Failed to open file: " << parsing_file;
203 return false;
204 }
205 uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
206 if (!dev_sz) {
207 LOG(ERROR) << "Could not determine block device size: " << parsing_file;
208 return false;
209 }
210
211 const int block_sz = 4_KiB;
212 dev_sz += block_sz - 1;
213 dev_sz &= ~(block_sz - 1);
214
215 auto npos = patchfile.rfind(".patch");
216 auto partition_name = patchfile.substr(0, npos);
217
218 chromeos_update_engine::DynamicPartitionGroup* group = nullptr;
219 std::string group_name = GetGroupName(*source_metadata.get(), partition_name);
220 if (group_map_.find(group_name) != group_map_.end()) {
221 group = group_map_[group_name];
222 } else {
223 group = dap->add_groups();
224 group->set_name(group_name);
225 group_map_[group_name] = group;
226 }
227 group->add_partition_names(partition_name);
228
229 auto pu = manifest_.mutable_partitions()->Add();
230 pu->set_partition_name(partition_name);
231 pu->set_estimate_cow_size(dev_sz);
232
233 CowReader reader;
234 if (!reader.Parse(fd)) {
235 LOG(ERROR) << "COW reader parse failed";
236 return false;
237 }
238
239 uint64_t new_device_size = 0;
240 const auto& header = reader.GetHeader();
241 if (header.prefix.major_version == 2) {
242 size_t num_ops = reader.get_num_total_data_ops();
243 new_device_size = (num_ops * header.block_size);
244 } else {
245 const auto& v3_header = reader.header_v3();
246 new_device_size = v3_header.op_count_max * v3_header.block_size;
247 }
248
249 LOG(INFO) << "Partition: " << partition_name << " Group_name: " << group_name
250 << " size: " << new_device_size << " COW-size: " << dev_sz;
251 pu->mutable_new_partition_info()->set_size(new_device_size);
252 }
253 return true;
254 }
255
GetCowDevicePath(std::string partition_name,std::string * cow_path)256 bool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {
257 auto& dm = android::dm::DeviceMapper::Instance();
258
259 std::string cow_device = partition_name + "-cow-img";
260 if (metadata_super_) {
261 // If COW device exists on /data, then data wipe cannot be done.
262 if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
263 LOG(ERROR) << "COW device exists on /data: " << *cow_path;
264 return false;
265 }
266 }
267
268 cow_device = partition_name + "-cow";
269 if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
270 return true;
271 }
272
273 LOG(INFO) << "Failed to find cow path: " << cow_device << " Checking the device for -img path";
274 // If the COW device exists only on /data
275 cow_device = partition_name + "-cow-img";
276 if (!dm.GetDmDevicePathByName(cow_device, cow_path)) {
277 LOG(ERROR) << "Failed to cow path: " << cow_device;
278 return false;
279 }
280 return true;
281 }
282
ApplyUpdate()283 bool MapSnapshots::ApplyUpdate() {
284 if (!PrepareUpdate()) {
285 LOG(ERROR) << "PrepareUpdate failed";
286 return false;
287 }
288 if (!sm_->BeginUpdate()) {
289 LOG(ERROR) << "BeginUpdate failed";
290 return false;
291 }
292 if (!sm_->CreateUpdateSnapshots(manifest_)) {
293 LOG(ERROR) << "Could not apply snapshots";
294 return false;
295 }
296
297 LOG(INFO) << "CreateUpdateSnapshots success";
298 if (!sm_->MapAllSnapshots(10s)) {
299 LOG(ERROR) << "MapAllSnapshots failed";
300 return false;
301 }
302
303 LOG(INFO) << "MapAllSnapshots success";
304
305 auto target_slot = fs_mgr_get_other_slot_suffix();
306 for (auto& patchfile : patchfiles_) {
307 auto npos = patchfile.rfind(".patch");
308 auto partition_name = patchfile.substr(0, npos) + target_slot;
309 std::string cow_path;
310 if (!GetCowDevicePath(partition_name, &cow_path)) {
311 LOG(ERROR) << "Failed to find cow path";
312 return false;
313 }
314 threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch,
315 this, cow_path, patchfile));
316 }
317
318 bool ret = true;
319 for (auto& t : threads_) {
320 ret = t.get() && ret;
321 }
322 if (!ret) {
323 LOG(ERROR) << "Snapshot writes failed";
324 return false;
325 }
326 if (!sm_->UnmapAllSnapshots()) {
327 LOG(ERROR) << "UnmapAllSnapshots failed";
328 return false;
329 }
330
331 LOG(INFO) << "Pre-created snapshots successfully copied";
332 // All snapshots have been written.
333 if (!sm_->FinishedSnapshotWrites(false /* wipe */)) {
334 LOG(ERROR) << "Could not finalize snapshot writes.\n";
335 return false;
336 }
337
338 auto hal = hal::BootControlClient::WaitForService();
339 if (!hal) {
340 LOG(ERROR) << "Could not find IBootControl HAL.\n";
341 return false;
342 }
343 auto target_slot_number = SlotNumberForSlotSuffix(target_slot);
344 auto cr = hal->SetActiveBootSlot(target_slot_number);
345 if (!cr.IsOk()) {
346 LOG(ERROR) << "Could not set active boot slot: " << cr.errMsg;
347 return false;
348 }
349
350 LOG(INFO) << "ApplyUpdate success";
351 return true;
352 }
353
BeginUpdate()354 bool MapSnapshots::BeginUpdate() {
355 if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
356 LOG(ERROR) << "Failed to create OTA metadata on super";
357 return false;
358 }
359 sm_ = SnapshotManager::New();
360
361 lock_ = sm_->LockExclusive();
362 std::vector<std::string> snapshots;
363 sm_->ListSnapshots(lock_.get(), &snapshots);
364 if (!snapshots.empty()) {
365 // Snapshots are already present.
366 return true;
367 }
368
369 lock_ = nullptr;
370 if (!sm_->BeginUpdate()) {
371 LOG(ERROR) << "BeginUpdate failed";
372 return false;
373 }
374 lock_ = sm_->LockExclusive();
375 return true;
376 }
377
CreateSnapshotDevice(std::string & partition_name,std::string & patchfile)378 bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {
379 std::string parsing_file = snapshot_dir_path_ + patchfile;
380
381 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
382 if (fd < 0) {
383 LOG(ERROR) << "Failed to open file: " << parsing_file;
384 return false;
385 }
386
387 uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
388 if (!dev_sz) {
389 LOG(ERROR) << "Could not determine block device size: " << parsing_file;
390 return false;
391 }
392
393 const int block_sz = 4_KiB;
394 dev_sz += block_sz - 1;
395 dev_sz &= ~(block_sz - 1);
396
397 SnapshotStatus status;
398 status.set_state(SnapshotState::CREATED);
399 status.set_using_snapuserd(true);
400 status.set_old_partition_size(0);
401 status.set_name(partition_name);
402 status.set_cow_file_size(dev_sz);
403 status.set_cow_partition_size(0);
404
405 PartitionCowCreator cow_creator;
406 cow_creator.using_snapuserd = true;
407
408 if (!sm_->CreateSnapshot(lock_.get(), &cow_creator, &status)) {
409 LOG(ERROR) << "CreateSnapshot failed";
410 return false;
411 }
412
413 if (!sm_->CreateCowImage(lock_.get(), partition_name)) {
414 LOG(ERROR) << "CreateCowImage failed";
415 return false;
416 }
417
418 return true;
419 }
420
GetCowImagePath(std::string & name)421 std::optional<std::string> MapSnapshots::GetCowImagePath(std::string& name) {
422 auto cow_dev = sm_->MapCowImage(name, 5s);
423 if (!cow_dev.has_value()) {
424 LOG(ERROR) << "Failed to get COW device path";
425 return std::nullopt;
426 }
427
428 LOG(INFO) << "COW Device path: " << cow_dev.value();
429 return cow_dev;
430 }
431
WriteSnapshotPatch(std::string cow_device,std::string patch)432 bool MapSnapshots::WriteSnapshotPatch(std::string cow_device, std::string patch) {
433 std::string patch_file = snapshot_dir_path_ + patch;
434
435 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(patch_file.c_str(), O_RDONLY)));
436 if (fd < 0) {
437 LOG(ERROR) << "Failed to open file: " << patch_file;
438 return false;
439 }
440
441 uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
442 if (!dev_sz) {
443 std::cout << "Could not determine block device size: " << patch_file;
444 return false;
445 }
446
447 android::base::unique_fd cfd(TEMP_FAILURE_RETRY(open(cow_device.c_str(), O_RDWR)));
448 if (cfd < 0) {
449 LOG(ERROR) << "Failed to open file: " << cow_device;
450 return false;
451 }
452
453 const uint64_t read_sz = 1_MiB;
454 std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
455 off_t file_offset = 0;
456
457 while (true) {
458 size_t to_read = std::min((dev_sz - file_offset), read_sz);
459 if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
460 PLOG(ERROR) << "ReadFullyAtOffset failed";
461 return false;
462 }
463
464 if (!android::base::WriteFullyAtOffset(cfd, buffer.get(), to_read, file_offset)) {
465 PLOG(ERROR) << "WriteFullyAtOffset failed";
466 return false;
467 }
468 file_offset += to_read;
469 if (file_offset >= dev_sz) {
470 break;
471 }
472 }
473 if (fsync(cfd.get()) < 0) {
474 PLOG(ERROR) << "Fsync failed";
475 return false;
476 }
477 return true;
478 }
479
InitiateThreadedSnapshotWrite(std::string & pname,std::string & snapshot_patch)480 bool MapSnapshots::InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch) {
481 auto path = GetCowImagePath(pname);
482 if (!path.has_value()) {
483 return false;
484 }
485 threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch, this,
486 path.value(), snapshot_patch));
487 return true;
488 }
489
FinishSnapshotWrites()490 bool MapSnapshots::FinishSnapshotWrites() {
491 bool ret = true;
492 for (auto& t : threads_) {
493 ret = t.get() && ret;
494 }
495
496 lock_ = nullptr;
497 if (ret) {
498 LOG(INFO) << "Pre-created snapshots successfully copied";
499 if (!sm_->FinishedSnapshotWrites(false)) {
500 return false;
501 }
502 return sm_->BootFromSnapshotsWithoutSlotSwitch();
503 }
504
505 LOG(ERROR) << "Snapshot copy failed";
506 return false;
507 }
508
UnmapCowImagePath(std::string & name)509 bool MapSnapshots::UnmapCowImagePath(std::string& name) {
510 sm_ = SnapshotManager::New();
511 return sm_->UnmapCowImage(name);
512 }
513
CleanupSnapshot()514 bool MapSnapshots::CleanupSnapshot() {
515 sm_ = SnapshotManager::New();
516 return sm_->PrepareDeviceToBootWithoutSnapshot();
517 }
518
DeleteSnapshots()519 bool MapSnapshots::DeleteSnapshots() {
520 sm_ = SnapshotManager::New();
521 lock_ = sm_->LockExclusive();
522 if (!sm_->RemoveAllUpdateState(lock_.get())) {
523 LOG(ERROR) << "Remove All Update State failed";
524 return false;
525 }
526 return true;
527 }
528 #endif
529
DumpCmdHandler(int,char ** argv)530 bool DumpCmdHandler(int /*argc*/, char** argv) {
531 android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
532 return SnapshotManager::New()->Dump(std::cout);
533 }
534
MapCmdHandler(int,char ** argv)535 bool MapCmdHandler(int, char** argv) {
536 android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
537 using namespace std::chrono_literals;
538 return SnapshotManager::New()->MapAllSnapshots(5000ms);
539 }
540
UnmapCmdHandler(int,char ** argv)541 bool UnmapCmdHandler(int, char** argv) {
542 android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
543 return SnapshotManager::New()->UnmapAllSnapshots();
544 }
545
PauseSnapshotMerge(int,char ** argv)546 bool PauseSnapshotMerge(int, char** argv) {
547 android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
548 return SnapshotManager::New()->PauseSnapshotMerge();
549 }
550
ResumeSnapshotMerge(int,char ** argv)551 bool ResumeSnapshotMerge(int, char** argv) {
552 android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
553 return SnapshotManager::New()->ResumeSnapshotMerge();
554 }
555
MergeCmdHandler(int,char ** argv)556 bool MergeCmdHandler(int /*argc*/, char** argv) {
557 android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
558 LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
559 return false;
560 }
561
562 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
GetVerityPartitions(std::vector<std::string> & partitions)563 bool GetVerityPartitions(std::vector<std::string>& partitions) {
564 auto& dm = android::dm::DeviceMapper::Instance();
565 auto dm_block_devices = dm.FindDmPartitions();
566 if (dm_block_devices.empty()) {
567 LOG(ERROR) << "No dm-enabled block device is found.";
568 return false;
569 }
570
571 for (auto& block_device : dm_block_devices) {
572 std::string dm_block_name = block_device.first;
573 std::string slot_suffix = fs_mgr_get_slot_suffix();
574 std::string partition = dm_block_name + slot_suffix;
575 partitions.push_back(partition);
576 }
577 return true;
578 }
579
UnMapPrecreatedSnapshots(int,char ** argv)580 bool UnMapPrecreatedSnapshots(int, char** argv) {
581 android::base::InitLogging(argv, &android::base::KernelLogger);
582 // Make sure we are root.
583 if (::getuid() != 0) {
584 LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
585 return EXIT_FAILURE;
586 }
587
588 std::vector<std::string> partitions;
589 if (!GetVerityPartitions(partitions)) {
590 return false;
591 }
592
593 MapSnapshots snapshot;
594 for (auto partition : partitions) {
595 if (!snapshot.UnmapCowImagePath(partition)) {
596 LOG(ERROR) << "UnmapCowImagePath failed: " << partition;
597 }
598 }
599 return true;
600 }
601
RemovePrecreatedSnapshots(int,char ** argv)602 bool RemovePrecreatedSnapshots(int, char** argv) {
603 android::base::InitLogging(argv, &android::base::KernelLogger);
604 // Make sure we are root.
605 if (::getuid() != 0) {
606 LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
607 return false;
608 }
609
610 MapSnapshots snapshot;
611 if (!snapshot.CleanupSnapshot()) {
612 LOG(ERROR) << "CleanupSnapshot failed";
613 return false;
614 }
615 return true;
616 }
617
DeletePrecreatedSnapshots(int,char ** argv)618 bool DeletePrecreatedSnapshots(int, char** argv) {
619 android::base::InitLogging(argv, &android::base::KernelLogger);
620 // Make sure we are root.
621 if (::getuid() != 0) {
622 LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
623 return EXIT_FAILURE;
624 }
625
626 MapSnapshots snapshot;
627 return snapshot.DeleteSnapshots();
628 }
629
ApplyUpdate(int argc,char ** argv)630 bool ApplyUpdate(int argc, char** argv) {
631 android::base::InitLogging(argv, &android::base::KernelLogger);
632
633 // Make sure we are root.
634 if (::getuid() != 0) {
635 LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
636 return EXIT_FAILURE;
637 }
638
639 if (argc < 3) {
640 std::cerr << " apply-update <directory location where snapshot patches are present> {-w}"
641 " Apply the snapshots to the COW block device\n";
642 return false;
643 }
644
645 std::string path = std::string(argv[2]);
646 bool metadata_on_super = false;
647 if (argc == 4) {
648 if (std::string(argv[3]) == "-w") {
649 metadata_on_super = true;
650 }
651 }
652 MapSnapshots cow(path, metadata_on_super);
653 if (!cow.ApplyUpdate()) {
654 return false;
655 }
656 LOG(INFO) << "Apply update success. Please reboot the device";
657 return true;
658 }
659
GetBlockHashFromMerkelTree(android::base::borrowed_fd image_fd,uint64_t image_size,uint32_t data_block_size,uint32_t hash_block_size,uint64_t tree_offset,std::vector<std::string> & out_block_hash)660 static bool GetBlockHashFromMerkelTree(android::base::borrowed_fd image_fd, uint64_t image_size,
661 uint32_t data_block_size, uint32_t hash_block_size,
662 uint64_t tree_offset,
663 std::vector<std::string>& out_block_hash) {
664 uint32_t padded_digest_size = 32;
665 if (image_size % data_block_size != 0) {
666 LOG(ERROR) << "Image_size: " << image_size
667 << " not a multiple of data block size: " << data_block_size;
668 return false;
669 }
670
671 // vector of level-size and offset
672 std::vector<std::pair<uint64_t, uint64_t>> levels;
673 uint64_t data_block_count = image_size / data_block_size;
674 uint32_t digests_per_block = hash_block_size / padded_digest_size;
675 uint32_t level_block_count = data_block_count;
676 while (level_block_count > 1) {
677 uint32_t next_level_block_count =
678 (level_block_count + digests_per_block - 1) / digests_per_block;
679 levels.emplace_back(std::make_pair(next_level_block_count * hash_block_size, 0));
680 level_block_count = next_level_block_count;
681 }
682 // root digest
683 levels.emplace_back(std::make_pair(0, 0));
684 // initialize offset
685 for (auto level = std::prev(levels.end()); level != levels.begin(); level--) {
686 std::prev(level)->second = level->second + level->first;
687 }
688
689 // We just want level 0
690 auto level = levels.begin();
691 std::string hash_block(hash_block_size, '\0');
692 uint64_t block_offset = tree_offset + level->second;
693 uint64_t t_read_blocks = 0;
694 uint64_t blockidx = 0;
695 uint64_t num_hash_blocks = level->first / hash_block_size;
696 while ((t_read_blocks < num_hash_blocks) && (blockidx < data_block_count)) {
697 if (!android::base::ReadFullyAtOffset(image_fd, hash_block.data(), hash_block.size(),
698 block_offset)) {
699 LOG(ERROR) << "Failed to read tree block at offset: " << block_offset;
700 return false;
701 }
702
703 for (uint32_t offset = 0; offset < hash_block.size(); offset += padded_digest_size) {
704 std::string single_hash = hash_block.substr(offset, padded_digest_size);
705 out_block_hash.emplace_back(single_hash);
706
707 blockidx += 1;
708 if (blockidx >= data_block_count) {
709 break;
710 }
711 }
712
713 block_offset += hash_block_size;
714 t_read_blocks += 1;
715 }
716 return true;
717 }
718
CalculateDigest(const void * buffer,size_t size,const void * salt,uint32_t salt_length,uint8_t * digest)719 static bool CalculateDigest(const void* buffer, size_t size, const void* salt, uint32_t salt_length,
720 uint8_t* digest) {
721 SHA256_CTX ctx;
722 if (SHA256_Init(&ctx) != 1) {
723 return false;
724 }
725 if (SHA256_Update(&ctx, salt, salt_length) != 1) {
726 return false;
727 }
728 if (SHA256_Update(&ctx, buffer, size) != 1) {
729 return false;
730 }
731 if (SHA256_Final(digest, &ctx) != 1) {
732 return false;
733 }
734 return true;
735 }
736
verify_data_blocks(android::base::borrowed_fd fd,const std::vector<std::string> & block_hash,std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor> & descriptor,const std::vector<uint8_t> & salt)737 bool verify_data_blocks(android::base::borrowed_fd fd, const std::vector<std::string>& block_hash,
738 std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor>& descriptor,
739 const std::vector<uint8_t>& salt) {
740 uint64_t data_block_count = descriptor->image_size / descriptor->data_block_size;
741 uint64_t foffset = 0;
742 uint64_t blk = 0;
743
744 std::string hash_block(descriptor->hash_block_size, '\0');
745 while (blk < data_block_count) {
746 if (!android::base::ReadFullyAtOffset(fd, hash_block.data(), descriptor->hash_block_size,
747 foffset)) {
748 LOG(ERROR) << "Failed to read from offset: " << foffset;
749 return false;
750 }
751
752 std::string digest(32, '\0');
753 CalculateDigest(hash_block.data(), descriptor->hash_block_size, salt.data(), salt.size(),
754 reinterpret_cast<uint8_t*>(digest.data()));
755 if (digest != block_hash[blk]) {
756 LOG(ERROR) << "Hash mismatch for block: " << blk << " Expected: " << block_hash[blk]
757 << " Received: " << digest;
758 return false;
759 }
760
761 foffset += descriptor->hash_block_size;
762 blk += 1;
763 }
764
765 return true;
766 }
767
DumpVerityHash(int argc,char ** argv)768 bool DumpVerityHash(int argc, char** argv) {
769 android::base::InitLogging(argv, &android::base::KernelLogger);
770
771 if (::getuid() != 0) {
772 LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
773 return EXIT_FAILURE;
774 }
775
776 if (argc < 3) {
777 std::cerr
778 << " dump-verity-hash <directory location where verity hash is saved> {-verify}\n";
779 return false;
780 }
781
782 bool verification_required = false;
783 std::string hash_file_path = argv[2];
784 bool metadata_on_super = false;
785 if (argc == 4) {
786 if (argv[3] == "-verify"s) {
787 verification_required = true;
788 }
789 }
790
791 auto& dm = android::dm::DeviceMapper::Instance();
792 auto dm_block_devices = dm.FindDmPartitions();
793 if (dm_block_devices.empty()) {
794 LOG(ERROR) << "No dm-enabled block device is found.";
795 return false;
796 }
797
798 android::fs_mgr::Fstab fstab;
799 if (!ReadDefaultFstab(&fstab)) {
800 LOG(ERROR) << "Failed to read fstab";
801 return false;
802 }
803
804 for (const auto& pair : dm_block_devices) {
805 std::string partition_name = pair.first;
806 android::fs_mgr::FstabEntry* fstab_entry =
807 GetEntryForMountPoint(&fstab, "/" + partition_name);
808 auto vbmeta = LoadAndVerifyVbmeta(*fstab_entry, "", nullptr, nullptr, nullptr);
809 if (vbmeta == nullptr) {
810 LOG(ERROR) << "LoadAndVerifyVbmetaByPath failed for partition: " << partition_name;
811 return false;
812 }
813
814 auto descriptor =
815 android::fs_mgr::GetHashtreeDescriptor(partition_name, std::move(*vbmeta));
816 if (descriptor == nullptr) {
817 LOG(ERROR) << "GetHashtreeDescriptor failed for partition: " << partition_name;
818 return false;
819 }
820
821 std::string device_path = fstab_entry->blk_device;
822 if (!dm.GetDmDevicePathByName(fstab_entry->blk_device, &device_path)) {
823 LOG(ERROR) << "Failed to resolve logical device path for: " << fstab_entry->blk_device;
824 return false;
825 }
826
827 android::base::unique_fd fd(open(device_path.c_str(), O_RDONLY));
828 if (fd < 0) {
829 LOG(ERROR) << "Failed to open file: " << device_path;
830 return false;
831 }
832 std::vector<std::string> block_hash;
833 if (!GetBlockHashFromMerkelTree(fd, descriptor->image_size, descriptor->data_block_size,
834 descriptor->hash_block_size, descriptor->tree_offset,
835 block_hash)) {
836 LOG(ERROR) << "GetBlockHashFromMerkelTree failed";
837 return false;
838 }
839
840 uint64_t dev_sz = lseek(fd, 0, SEEK_END);
841 uint64_t fec_size = dev_sz - descriptor->image_size;
842 if (fec_size % descriptor->data_block_size != 0) {
843 LOG(ERROR) << "fec_size: " << fec_size
844 << " isn't multiple of: " << descriptor->data_block_size;
845 return false;
846 }
847
848 std::vector<uint8_t> salt;
849 const std::string& salt_str = descriptor->salt;
850 bool ok = android::base::HexToBytes(salt_str, &salt);
851 if (!ok) {
852 LOG(ERROR) << "HexToBytes conversion failed";
853 return false;
854 }
855 uint64_t file_offset = descriptor->image_size;
856 std::vector<uint8_t> hash_block(descriptor->hash_block_size, 0);
857 while (file_offset < dev_sz) {
858 if (!android::base::ReadFullyAtOffset(fd, hash_block.data(),
859 descriptor->hash_block_size, file_offset)) {
860 LOG(ERROR) << "Failed to read tree block at offset: " << file_offset;
861 return false;
862 }
863 std::string digest(32, '\0');
864 CalculateDigest(hash_block.data(), descriptor->hash_block_size, salt.data(),
865 salt.size(), reinterpret_cast<uint8_t*>(digest.data()));
866 block_hash.push_back(digest);
867 file_offset += descriptor->hash_block_size;
868 fec_size -= descriptor->hash_block_size;
869 }
870
871 if (fec_size != 0) {
872 LOG(ERROR) << "Checksum calculation pending: " << fec_size;
873 return false;
874 }
875
876 if (verification_required) {
877 if (!verify_data_blocks(fd, block_hash, descriptor, salt)) {
878 LOG(ERROR) << "verify_data_blocks failed";
879 return false;
880 }
881 }
882
883 VerityHash verity_hash;
884 verity_hash.set_partition_name(partition_name);
885 verity_hash.set_salt(salt_str);
886 for (auto hash : block_hash) {
887 verity_hash.add_block_hash(hash.data(), hash.size());
888 }
889 std::string hash_file = hash_file_path + "/" + partition_name + ".pb";
890 std::string content;
891 if (!verity_hash.SerializeToString(&content)) {
892 LOG(ERROR) << "Unable to serialize verity_hash";
893 return false;
894 }
895 if (!WriteStringToFileAtomic(content, hash_file)) {
896 PLOG(ERROR) << "Unable to write VerityHash to " << hash_file;
897 return false;
898 }
899
900 LOG(INFO) << partition_name
901 << ": GetBlockHashFromMerkelTree success. Num Blocks: " << block_hash.size();
902 }
903 return true;
904 }
905
MapPrecreatedSnapshots(int argc,char ** argv)906 bool MapPrecreatedSnapshots(int argc, char** argv) {
907 android::base::InitLogging(argv, &android::base::KernelLogger);
908
909 // Make sure we are root.
910 if (::getuid() != 0) {
911 LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
912 return EXIT_FAILURE;
913 }
914
915 if (argc < 3) {
916 std::cerr << " map-snapshots <directory location where snapshot patches are present> {-w}"
917 " Map all snapshots based on patches present in the directory\n";
918 return false;
919 }
920
921 std::string path = std::string(argv[2]);
922 std::vector<std::string> patchfiles;
923
924 for (const auto& entry : std::filesystem::directory_iterator(path)) {
925 if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
926 patchfiles.push_back(android::base::Basename(entry.path().generic_string()));
927 }
928 }
929 auto& dm = android::dm::DeviceMapper::Instance();
930 auto dm_block_devices = dm.FindDmPartitions();
931 if (dm_block_devices.empty()) {
932 LOG(ERROR) << "No dm-enabled block device is found.";
933 return false;
934 }
935
936 std::vector<std::pair<std::string, std::string>> partitions;
937 for (auto& patchfile : patchfiles) {
938 auto npos = patchfile.rfind(".patch");
939 auto dm_block_name = patchfile.substr(0, npos);
940 if (dm_block_devices.find(dm_block_name) != dm_block_devices.end()) {
941 std::string slot_suffix = fs_mgr_get_slot_suffix();
942 std::string partition = dm_block_name + slot_suffix;
943 partitions.push_back(std::make_pair(partition, patchfile));
944 }
945 }
946
947 bool metadata_on_super = false;
948 if (argc == 4) {
949 if (std::string(argv[3]) == "-w") {
950 metadata_on_super = true;
951 }
952 }
953
954 MapSnapshots cow(path, metadata_on_super);
955 if (!cow.BeginUpdate()) {
956 LOG(ERROR) << "BeginUpdate failed";
957 return false;
958 }
959
960 for (auto& pair : partitions) {
961 if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {
962 LOG(ERROR) << "CreateSnapshotDevice failed for: " << pair.first;
963 return false;
964 }
965 if (!cow.InitiateThreadedSnapshotWrite(pair.first, pair.second)) {
966 LOG(ERROR) << "InitiateThreadedSnapshotWrite failed for: " << pair.first;
967 return false;
968 }
969 }
970
971 return cow.FinishSnapshotWrites();
972 }
973
CreateTestUpdate(SnapshotManager * sm)974 bool CreateTestUpdate(SnapshotManager* sm) {
975 chromeos_update_engine::DeltaArchiveManifest manifest;
976
977 // We only copy system, to simplify things.
978 manifest.set_partial_update(true);
979
980 auto dap = manifest.mutable_dynamic_partition_metadata();
981 dap->set_snapshot_enabled(true);
982 dap->set_vabc_enabled(true);
983 dap->set_vabc_compression_param("none");
984 dap->set_cow_version(kCowVersionMajor);
985
986 auto source_slot = fs_mgr_get_slot_suffix();
987 auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
988 auto target_slot = fs_mgr_get_other_slot_suffix();
989 auto target_slot_number = SlotNumberForSlotSuffix(target_slot);
990 auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
991
992 // Get current partition information.
993 PartitionOpener opener;
994 auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);
995 if (!source_metadata) {
996 std::cerr << "Could not read source partition metadata.\n";
997 return false;
998 }
999
1000 auto system_source_name = "system" + source_slot;
1001 auto system_source = FindPartition(*source_metadata.get(), system_source_name);
1002 if (!system_source) {
1003 std::cerr << "Could not find system partition: " << system_source_name << ".\n";
1004 return false;
1005 }
1006 auto system_source_size = GetPartitionSize(*source_metadata.get(), *system_source);
1007
1008 // Since we only add copy operations, 64MB should be enough.
1009 auto system_update = manifest.mutable_partitions()->Add();
1010 system_update->set_partition_name("system");
1011 system_update->set_estimate_cow_size(64_MiB);
1012 system_update->mutable_new_partition_info()->set_size(system_source_size);
1013
1014 if (!sm->CreateUpdateSnapshots(manifest)) {
1015 std::cerr << "Could not create update snapshots.\n";
1016 return false;
1017 }
1018
1019 // Write the "new" system partition.
1020 auto system_target_name = "system" + target_slot;
1021 CreateLogicalPartitionParams clpp = {
1022 .block_device = fs_mgr_get_super_partition_name(target_slot_number),
1023 .metadata_slot = {target_slot_number},
1024 .partition_name = system_target_name,
1025 .timeout_ms = 10s,
1026 .partition_opener = &opener,
1027 };
1028 auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);
1029 if (!writer) {
1030 std::cerr << "Could not open snapshot writer.\n";
1031 return false;
1032 }
1033
1034 for (uint64_t block = 0; block < system_source_size / 4096; block++) {
1035 if (!writer->AddCopy(block, block)) {
1036 std::cerr << "Unable to add copy operation for block " << block << ".\n";
1037 return false;
1038 }
1039 }
1040 if (!writer->Finalize()) {
1041 std::cerr << "Could not finalize COW for " << system_target_name << ".\n";
1042 return false;
1043 }
1044 writer = nullptr;
1045
1046 // Finished writing this partition, unmap.
1047 if (!sm->UnmapUpdateSnapshot(system_target_name)) {
1048 std::cerr << "Could not unmap snapshot for " << system_target_name << ".\n";
1049 return false;
1050 }
1051
1052 // All snapshots have been written.
1053 if (!sm->FinishedSnapshotWrites(false /* wipe */)) {
1054 std::cerr << "Could not finalize snapshot writes.\n";
1055 return false;
1056 }
1057
1058 auto hal = hal::BootControlClient::WaitForService();
1059 if (!hal) {
1060 std::cerr << "Could not find IBootControl HAL.\n";
1061 return false;
1062 }
1063 auto cr = hal->SetActiveBootSlot(target_slot_number);
1064 if (!cr.IsOk()) {
1065 std::cerr << "Could not set active boot slot: " << cr.errMsg;
1066 return false;
1067 }
1068
1069 std::cerr << "It is now safe to reboot your device. If using a physical device, make\n"
1070 << "sure that all physical partitions are flashed to both A and B slots.\n";
1071 return true;
1072 }
1073
TestOtaHandler(int,char **)1074 bool TestOtaHandler(int /* argc */, char** /* argv */) {
1075 auto sm = SnapshotManager::New();
1076
1077 if (!sm->BeginUpdate()) {
1078 std::cerr << "Error starting update.\n";
1079 return false;
1080 }
1081
1082 if (!CreateTestUpdate(sm.get())) {
1083 sm->CancelUpdate();
1084 return false;
1085 }
1086 return true;
1087 }
1088 #endif
1089
1090 static std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {
1091 // clang-format off
1092 {"dump", DumpCmdHandler},
1093 {"merge", MergeCmdHandler},
1094 {"map", MapCmdHandler},
1095 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
1096 {"test-blank-ota", TestOtaHandler},
1097 {"apply-update", ApplyUpdate},
1098 {"map-snapshots", MapPrecreatedSnapshots},
1099 {"unmap-snapshots", UnMapPrecreatedSnapshots},
1100 {"delete-snapshots", DeletePrecreatedSnapshots},
1101 {"revert-snapshots", RemovePrecreatedSnapshots},
1102 {"dump-verity-hash", DumpVerityHash},
1103 #endif
1104 {"unmap", UnmapCmdHandler},
1105 {"pause-merge", PauseSnapshotMerge},
1106 {"resume-merge", ResumeSnapshotMerge},
1107 // clang-format on
1108 };
1109
1110 } // namespace snapshot
1111 } // namespace android
1112
main(int argc,char ** argv)1113 int main(int argc, char** argv) {
1114 using namespace android::snapshot;
1115 if (argc < 2) {
1116 return Usage();
1117 }
1118
1119 for (const auto& cmd : kCmdMap) {
1120 if (cmd.first == argv[1]) {
1121 return cmd.second(argc, argv) ? EX_OK : EX_SOFTWARE;
1122 }
1123 }
1124
1125 return Usage();
1126 }
1127