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 <libfiemap/image_manager.h>
18
19 #include <optional>
20
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <android-base/properties.h>
24 #include <android-base/strings.h>
25 #include <android-base/unique_fd.h>
26 #include <ext4_utils/ext4_utils.h>
27 #include <fs_mgr/file_wait.h>
28 #include <fs_mgr_dm_linear.h>
29 #include <libdm/loop_control.h>
30 #include <libfiemap/split_fiemap_writer.h>
31 #include <libgsi/libgsi.h>
32
33 #include "metadata.h"
34 #include "utility.h"
35
36 namespace android {
37 namespace fiemap {
38
39 using namespace std::literals;
40 using android::base::ReadFileToString;
41 using android::base::unique_fd;
42 using android::dm::DeviceMapper;
43 using android::dm::DmDeviceState;
44 using android::dm::DmTable;
45 using android::dm::DmTargetLinear;
46 using android::dm::LoopControl;
47 using android::fs_mgr::CreateLogicalPartition;
48 using android::fs_mgr::CreateLogicalPartitionParams;
49 using android::fs_mgr::CreateLogicalPartitions;
50 using android::fs_mgr::DestroyLogicalPartition;
51 using android::fs_mgr::GetBlockDevicePartitionName;
52 using android::fs_mgr::GetBlockDevicePartitionNames;
53 using android::fs_mgr::GetPartitionName;
54
55 static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
56 static constexpr char kOtaTestImageMetadataDir[] = "/metadata/gsi/ota/test";
57
Open(const std::string & dir_prefix,const DeviceInfo & device_info)58 std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix,
59 const DeviceInfo& device_info) {
60 auto metadata_dir = "/metadata/gsi/" + dir_prefix;
61 auto data_dir = "/data/gsi/" + dir_prefix;
62 auto install_dir_file = gsi::DsuInstallDirFile(gsi::GetDsuSlot(dir_prefix));
63 std::string path;
64 if (ReadFileToString(install_dir_file, &path)) {
65 data_dir = path;
66 }
67 return Open(metadata_dir, data_dir, device_info);
68 }
69
Open(const std::string & metadata_dir,const std::string & data_dir,const DeviceInfo & device_info)70 std::unique_ptr<ImageManager> ImageManager::Open(const std::string& metadata_dir,
71 const std::string& data_dir,
72 const DeviceInfo& device_info) {
73 return std::unique_ptr<ImageManager>(new ImageManager(metadata_dir, data_dir, device_info));
74 }
75
ImageManager(const std::string & metadata_dir,const std::string & data_dir,const DeviceInfo & device_info)76 ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir,
77 const DeviceInfo& device_info)
78 : metadata_dir_(metadata_dir), data_dir_(data_dir), device_info_(device_info) {
79 partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
80
81 // Allow overriding whether ImageManager thinks it's in recovery, for testing.
82 #ifdef __ANDROID_RECOVERY__
83 device_info_.is_recovery = {true};
84 #else
85 if (!device_info_.is_recovery.has_value()) {
86 device_info_.is_recovery = {false};
87 }
88 #endif
89 }
90
GetImageHeaderPath(const std::string & name)91 std::string ImageManager::GetImageHeaderPath(const std::string& name) {
92 return JoinPaths(data_dir_, name) + ".img";
93 }
94
95 // The status file has one entry per line, with each entry formatted as one of:
96 // dm:<name>
97 // loop:<path>
98 //
99 // This simplifies the process of tearing down a mapping, since we can simply
100 // unmap each entry in the order it appears.
GetStatusFilePath(const std::string & image_name)101 std::string ImageManager::GetStatusFilePath(const std::string& image_name) {
102 return JoinPaths(metadata_dir_, image_name) + ".status";
103 }
104
GetStatusPropertyName(const std::string & image_name)105 static std::string GetStatusPropertyName(const std::string& image_name) {
106 // Note: we don't prefix |image_name|, because CreateLogicalPartition won't
107 // prefix the name either. There are no plans to change this at the moment,
108 // consumers of the image API must take care to use globally-unique image
109 // names.
110 return "gsid.mapped_image." + image_name;
111 }
112
set_partition_opener(std::unique_ptr<IPartitionOpener> && opener)113 void ImageManager::set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener) {
114 partition_opener_ = std::move(opener);
115 }
116
IsImageMapped(const std::string & image_name)117 bool ImageManager::IsImageMapped(const std::string& image_name) {
118 auto prop_name = GetStatusPropertyName(image_name);
119 if (android::base::GetProperty(prop_name, "").empty()) {
120 // If mapped in first-stage init, the dm-device will exist but not the
121 // property.
122 auto& dm = DeviceMapper::Instance();
123 return dm.GetState(image_name) != DmDeviceState::INVALID;
124 }
125 return true;
126 }
127
GetAllBackingImages()128 std::vector<std::string> ImageManager::GetAllBackingImages() {
129 std::vector<std::string> images;
130 if (!MetadataExists(metadata_dir_)) {
131 return images;
132 }
133 auto metadata = OpenMetadata(metadata_dir_);
134 if (metadata) {
135 for (auto&& partition : metadata->partitions) {
136 images.push_back(partition.name);
137 }
138 }
139 return images;
140 }
141
BackingImageExists(const std::string & name)142 bool ImageManager::BackingImageExists(const std::string& name) {
143 if (!MetadataExists(metadata_dir_)) {
144 return false;
145 }
146 auto metadata = OpenMetadata(metadata_dir_);
147 if (!metadata) {
148 return false;
149 }
150 return !!FindPartition(*metadata.get(), name);
151 }
152
MetadataDirIsTest() const153 bool ImageManager::MetadataDirIsTest() const {
154 return IsSubdir(metadata_dir_, kTestImageMetadataDir) ||
155 IsSubdir(metadata_dir_, kOtaTestImageMetadataDir);
156 }
157
IsUnreliablePinningAllowed() const158 bool ImageManager::IsUnreliablePinningAllowed() const {
159 return IsSubdir(data_dir_, "/data/gsi/dsu/") || MetadataDirIsTest();
160 }
161
CreateBackingImage(const std::string & name,uint64_t size,int flags,std::function<bool (uint64_t,uint64_t)> && on_progress)162 FiemapStatus ImageManager::CreateBackingImage(
163 const std::string& name, uint64_t size, int flags,
164 std::function<bool(uint64_t, uint64_t)>&& on_progress) {
165 auto data_path = GetImageHeaderPath(name);
166 std::unique_ptr<SplitFiemap> fw;
167 auto status = SplitFiemap::Create(data_path, size, 0, &fw, on_progress);
168 if (!status.is_ok()) {
169 return status;
170 }
171
172 bool reliable_pinning;
173 if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {
174 return FiemapStatus::Error();
175 }
176 if (!reliable_pinning && !IsUnreliablePinningAllowed()) {
177 // For historical reasons, we allow unreliable pinning for certain use
178 // cases (DSUs, testing) because the ultimate use case is either
179 // developer-oriented or ephemeral (the intent is to boot immediately
180 // into DSUs). For everything else - such as snapshots/OTAs or adb
181 // remount, we have a higher bar, and require the filesystem to support
182 // proper pinning.
183 LOG(ERROR) << "File system does not have reliable block pinning";
184 SplitFiemap::RemoveSplitFiles(data_path);
185 return FiemapStatus::Error();
186 }
187
188 // Except for testing, we do not allow persisting metadata that references
189 // device-mapper devices. It just doesn't make sense, because the device
190 // numbering may change on reboot. We allow it for testing since the images
191 // are not meant to survive reboot. Outside of tests, this can only happen
192 // if device-mapper is stacked in some complex way not supported by
193 // FiemapWriter.
194 auto device_path = GetDevicePathForFile(fw.get());
195 if (android::base::StartsWith(device_path, "/dev/block/dm-") && !MetadataDirIsTest()) {
196 LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
197
198 fw = {};
199 SplitFiemap::RemoveSplitFiles(data_path);
200 return FiemapStatus::Error();
201 }
202
203 bool readonly = !!(flags & CREATE_IMAGE_READONLY);
204 if (!UpdateMetadata(metadata_dir_, name, fw.get(), size, readonly)) {
205 return FiemapStatus::Error();
206 }
207
208 if (flags & CREATE_IMAGE_ZERO_FILL) {
209 auto res = ZeroFillNewImage(name, 0);
210 if (!res.is_ok()) {
211 DeleteBackingImage(name);
212 return res;
213 }
214 }
215 return FiemapStatus::Ok();
216 }
217
ZeroFillNewImage(const std::string & name,uint64_t bytes)218 FiemapStatus ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
219 auto data_path = GetImageHeaderPath(name);
220
221 // See the comment in MapImageDevice() about how this works.
222 std::string block_device;
223 bool can_use_devicemapper;
224 if (!FiemapWriter::GetBlockDeviceForFile(data_path, &block_device, &can_use_devicemapper)) {
225 LOG(ERROR) << "Could not determine block device for " << data_path;
226 return FiemapStatus::Error();
227 }
228
229 if (!can_use_devicemapper) {
230 // We've backed with loop devices, and since we store files in an
231 // unencrypted folder, the initial zeroes we wrote will suffice.
232 return FiemapStatus::Ok();
233 }
234
235 // data is dm-crypt, or FBE + dm-default-key. This means the zeroes written
236 // by libfiemap were encrypted, so we need to map the image in and correct
237 // this.
238 auto device = MappedDevice::Open(this, 10s, name);
239 if (!device) {
240 return FiemapStatus::Error();
241 }
242
243 static constexpr size_t kChunkSize = 4096;
244 std::string zeroes(kChunkSize, '\0');
245
246 uint64_t remaining;
247 if (bytes) {
248 remaining = bytes;
249 } else {
250 remaining = get_block_device_size(device->fd());
251 if (!remaining) {
252 PLOG(ERROR) << "Could not get block device size for " << device->path();
253 return FiemapStatus::FromErrno(errno);
254 }
255 }
256 while (remaining) {
257 uint64_t to_write = std::min(static_cast<uint64_t>(zeroes.size()), remaining);
258 if (!android::base::WriteFully(device->fd(), zeroes.data(),
259 static_cast<size_t>(to_write))) {
260 PLOG(ERROR) << "write failed: " << device->path();
261 return FiemapStatus::FromErrno(errno);
262 }
263 remaining -= to_write;
264 }
265 return FiemapStatus::Ok();
266 }
267
DeleteBackingImage(const std::string & name)268 bool ImageManager::DeleteBackingImage(const std::string& name) {
269 // For dm-linear devices sitting on top of /data, we cannot risk deleting
270 // the file. The underlying blocks could be reallocated by the filesystem.
271 if (IsImageMapped(name)) {
272 LOG(ERROR) << "Cannot delete backing image " << name << " because mapped to a block device";
273 return false;
274 }
275
276 if (device_info_.is_recovery.value()) {
277 LOG(ERROR) << "Cannot remove images backed by /data in recovery";
278 return false;
279 }
280
281 std::string message;
282 auto header_file = GetImageHeaderPath(name);
283 if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) {
284 // This is fatal, because we don't want to leave these files dangling.
285 LOG(ERROR) << "Error removing image " << name << ": " << message;
286 return false;
287 }
288
289 auto status_file = GetStatusFilePath(name);
290 if (!android::base::RemoveFileIfExists(status_file)) {
291 LOG(ERROR) << "Error removing " << status_file << ": " << message;
292 }
293 return RemoveImageMetadata(metadata_dir_, name);
294 }
295
296 // Create a block device for an image file, using its extents in its
297 // lp_metadata.
MapWithDmLinear(const IPartitionOpener & opener,const std::string & name,const std::chrono::milliseconds & timeout_ms,std::string * path)298 bool ImageManager::MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
299 const std::chrono::milliseconds& timeout_ms, std::string* path) {
300 // :TODO: refresh extents in metadata file until f2fs is fixed.
301 auto metadata = OpenMetadata(metadata_dir_);
302 if (!metadata) {
303 return false;
304 }
305
306 auto super = android::fs_mgr::GetMetadataSuperBlockDevice(*metadata.get());
307 auto block_device = android::fs_mgr::GetBlockDevicePartitionName(*super);
308
309 CreateLogicalPartitionParams params = {
310 .block_device = block_device,
311 .metadata = metadata.get(),
312 .partition_name = name,
313 .force_writable = true,
314 .timeout_ms = timeout_ms,
315 .partition_opener = &opener,
316 };
317 if (!CreateLogicalPartition(params, path)) {
318 LOG(ERROR) << "Error creating device-mapper node for image " << name;
319 return false;
320 }
321
322 auto status_string = "dm:" + name;
323 auto status_file = GetStatusFilePath(name);
324 if (!android::base::WriteStringToFile(status_string, status_file)) {
325 PLOG(ERROR) << "Could not write status file: " << status_file;
326 DestroyLogicalPartition(name);
327 return false;
328 }
329 return true;
330 }
331
332 // Helper to create a loop device for a file.
CreateLoopDevice(LoopControl & control,const std::string & file,const std::chrono::milliseconds & timeout_ms,std::string * path)333 static bool CreateLoopDevice(LoopControl& control, const std::string& file,
334 const std::chrono::milliseconds& timeout_ms, std::string* path) {
335 static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
336 android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
337 if (file_fd < 0) {
338 PLOG(ERROR) << "Could not open file: " << file;
339 return false;
340 }
341 if (!control.Attach(file_fd, timeout_ms, path)) {
342 LOG(ERROR) << "Could not create loop device for: " << file;
343 return false;
344 }
345 LOG(INFO) << "Created loop device " << *path << " for file " << file;
346 return true;
347 }
348
349 class AutoDetachLoopDevices final {
350 public:
AutoDetachLoopDevices(LoopControl & control,const std::vector<std::string> & devices)351 AutoDetachLoopDevices(LoopControl& control, const std::vector<std::string>& devices)
352 : control_(control), devices_(devices), commit_(false) {}
353
~AutoDetachLoopDevices()354 ~AutoDetachLoopDevices() {
355 if (commit_) return;
356 for (const auto& device : devices_) {
357 control_.Detach(device);
358 }
359 }
360
Commit()361 void Commit() { commit_ = true; }
362
363 private:
364 LoopControl& control_;
365 const std::vector<std::string>& devices_;
366 bool commit_;
367 };
368
369 // If an image is stored across multiple files, this takes a list of loop
370 // devices and joins them together using device-mapper.
MapWithLoopDeviceList(const std::vector<std::string> & device_list,const std::string & name,const std::chrono::milliseconds & timeout_ms,std::string * path)371 bool ImageManager::MapWithLoopDeviceList(const std::vector<std::string>& device_list,
372 const std::string& name,
373 const std::chrono::milliseconds& timeout_ms,
374 std::string* path) {
375 auto metadata = OpenMetadata(metadata_dir_);
376 if (!metadata) {
377 return false;
378 }
379 auto partition = FindPartition(*metadata.get(), name);
380 if (!partition) {
381 LOG(ERROR) << "Could not find image in metadata: " << name;
382 return false;
383 }
384
385 // Since extent lengths are in sector units, the size should be a multiple
386 // of the sector size.
387 uint64_t partition_size = GetPartitionSize(*metadata.get(), *partition);
388 if (partition_size % LP_SECTOR_SIZE != 0) {
389 LOG(ERROR) << "Partition size not sector aligned: " << name << ", " << partition_size
390 << " bytes";
391 return false;
392 }
393
394 DmTable table;
395
396 uint64_t start_sector = 0;
397 uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;
398 for (const auto& block_device : device_list) {
399 // The final block device must be == partition_size, otherwise we
400 // can't find the AVB footer on verified partitions.
401 static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
402 unique_fd fd(open(block_device.c_str(), kOpenFlags));
403 if (fd < 0) {
404 PLOG(ERROR) << "Open failed: " << block_device;
405 return false;
406 }
407
408 uint64_t file_size = get_block_device_size(fd);
409 uint64_t file_sectors = file_size / LP_SECTOR_SIZE;
410 uint64_t segment_size = std::min(file_sectors, sectors_needed);
411
412 table.Emplace<DmTargetLinear>(start_sector, segment_size, block_device, 0);
413
414 start_sector += segment_size;
415 sectors_needed -= segment_size;
416 if (sectors_needed == 0) {
417 break;
418 }
419 }
420
421 auto& dm = DeviceMapper::Instance();
422 if (!dm.CreateDevice(name, table, path, timeout_ms)) {
423 LOG(ERROR) << "Could not create device-mapper device over loop set";
424 return false;
425 }
426
427 // Build the status file.
428 std::vector<std::string> lines;
429 lines.emplace_back("dm:" + name);
430 for (const auto& block_device : device_list) {
431 lines.emplace_back("loop:" + block_device);
432 }
433 auto status_message = android::base::Join(lines, "\n");
434 auto status_file = GetStatusFilePath(name);
435 if (!android::base::WriteStringToFile(status_message, status_file)) {
436 PLOG(ERROR) << "Write failed: " << status_file;
437 dm.DeleteDevice(name);
438 return false;
439 }
440 return true;
441 }
442
OptimizeLoopDevices(const std::vector<std::string> & device_list)443 static bool OptimizeLoopDevices(const std::vector<std::string>& device_list) {
444 for (const auto& device : device_list) {
445 unique_fd fd(open(device.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW));
446 if (fd < 0) {
447 PLOG(ERROR) << "Open failed: " << device;
448 return false;
449 }
450 if (!LoopControl::EnableDirectIo(fd)) {
451 return false;
452 }
453 }
454 return true;
455 }
456
457 // Helper to use one or more loop devices around image files.
MapWithLoopDevice(const std::string & name,const std::chrono::milliseconds & timeout_ms,std::string * path)458 bool ImageManager::MapWithLoopDevice(const std::string& name,
459 const std::chrono::milliseconds& timeout_ms,
460 std::string* path) {
461 auto image_header = GetImageHeaderPath(name);
462
463 std::vector<std::string> file_list;
464 if (!SplitFiemap::GetSplitFileList(image_header, &file_list)) {
465 LOG(ERROR) << "Could not get image file list";
466 return false;
467 }
468
469 // Map each image file as a loopback device.
470 LoopControl control;
471 std::vector<std::string> loop_devices;
472 AutoDetachLoopDevices auto_detach(control, loop_devices);
473
474 auto start_time = std::chrono::steady_clock::now();
475 for (const auto& file : file_list) {
476 auto now = std::chrono::steady_clock::now();
477 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
478
479 std::string loop_device;
480 if (!CreateLoopDevice(control, file, timeout_ms - elapsed, &loop_device)) {
481 break;
482 }
483 loop_devices.emplace_back(loop_device);
484 }
485 if (loop_devices.size() != file_list.size()) {
486 // The number of devices will mismatch if CreateLoopDevice() failed.
487 return false;
488 }
489
490 // If OptimizeLoopDevices fails, we'd use double the memory.
491 if (!OptimizeLoopDevices(loop_devices)) {
492 return false;
493 }
494
495 // If there's only one loop device (by far the most common case, splits
496 // will normally only happen on sdcards with FAT32), then just return that
497 // as the block device. Otherwise, we need to use dm-linear to stitch
498 // together all the loop devices we just created.
499 if (loop_devices.size() > 1) {
500 if (!MapWithLoopDeviceList(loop_devices, name, timeout_ms, path)) {
501 return false;
502 }
503 } else {
504 auto status_message = "loop:" + loop_devices.back();
505 auto status_file = GetStatusFilePath(name);
506 if (!android::base::WriteStringToFile(status_message, status_file)) {
507 PLOG(ERROR) << "Write failed: " << status_file;
508 return false;
509 }
510 }
511 auto_detach.Commit();
512
513 *path = loop_devices.back();
514 return true;
515 }
516
MapImageDevice(const std::string & name,const std::chrono::milliseconds & timeout_ms,std::string * path)517 bool ImageManager::MapImageDevice(const std::string& name,
518 const std::chrono::milliseconds& timeout_ms, std::string* path) {
519 if (IsImageMapped(name)) {
520 LOG(ERROR) << "Backing image " << name << " is already mapped";
521 return false;
522 }
523
524 auto image_header = GetImageHeaderPath(name);
525
526 #if !defined __ANDROID_RECOVERY__
527 // If there is a device-mapper node wrapping the block device, then we're
528 // able to create another node around it; the dm layer does not carry the
529 // exclusion lock down the stack when a mount occurs.
530 //
531 // If there is no intermediate device-mapper node, then partitions cannot be
532 // opened writable due to sepolicy and exclusivity of having a mounted
533 // filesystem. This should only happen on devices with no encryption, or
534 // devices with FBE and no metadata encryption. For these cases it suffices
535 // to perform normal file writes to /data/gsi (which is unencrypted).
536 //
537 // Note: this is not gated on DeviceInfo, because the recovery-specific path
538 // must only be used in actual recovery.
539 std::string block_device;
540 bool can_use_devicemapper;
541 if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) {
542 LOG(ERROR) << "Could not determine block device for " << image_header;
543 return false;
544 }
545
546 if (can_use_devicemapper) {
547 if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
548 return false;
549 }
550 } else if (!MapWithLoopDevice(name, timeout_ms, path)) {
551 return false;
552 }
553 #else
554 // In recovery, we can *only* use device-mapper, since partitions aren't
555 // mounted. That also means we cannot call GetBlockDeviceForFile.
556 if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
557 return false;
558 }
559 #endif
560
561 // Set a property so we remember this is mapped.
562 auto prop_name = GetStatusPropertyName(name);
563 if (!android::base::SetProperty(prop_name, *path)) {
564 UnmapImageDevice(name, true);
565 return false;
566 }
567 return true;
568 }
569
MapImageWithDeviceMapper(const IPartitionOpener & opener,const std::string & name,std::string * dev)570 bool ImageManager::MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
571 std::string* dev) {
572 std::string ignore_path;
573 if (!MapWithDmLinear(opener, name, {}, &ignore_path)) {
574 return false;
575 }
576
577 auto& dm = DeviceMapper::Instance();
578 if (!dm.GetDeviceString(name, dev)) {
579 return false;
580 }
581 return true;
582 }
583
UnmapImageDevice(const std::string & name)584 bool ImageManager::UnmapImageDevice(const std::string& name) {
585 return UnmapImageDevice(name, false);
586 }
587
UnmapImageDevice(const std::string & name,bool force)588 bool ImageManager::UnmapImageDevice(const std::string& name, bool force) {
589 if (!force && !IsImageMapped(name)) {
590 LOG(ERROR) << "Backing image " << name << " is not mapped";
591 return false;
592 }
593 auto& dm = DeviceMapper::Instance();
594 std::optional<LoopControl> loop;
595
596 std::string status;
597 auto status_file = GetStatusFilePath(name);
598 if (!android::base::ReadFileToString(status_file, &status)) {
599 PLOG(ERROR) << "Read failed: " << status_file;
600 return false;
601 }
602
603 auto lines = android::base::Split(status, "\n");
604 for (const auto& line : lines) {
605 auto pieces = android::base::Split(line, ":");
606 if (pieces.size() != 2) {
607 LOG(ERROR) << "Unknown status line";
608 continue;
609 }
610 if (pieces[0] == "dm") {
611 // Failure to remove a dm node is fatal, since we can't safely
612 // remove the file or loop devices.
613 const auto& name = pieces[1];
614 if (!dm.DeleteDeviceIfExists(name)) {
615 return false;
616 }
617 } else if (pieces[0] == "loop") {
618 // Lazily connect to loop-control to avoid spurious errors in recovery.
619 if (!loop.has_value()) {
620 loop.emplace();
621 }
622
623 // Failure to remove a loop device is not fatal, since we can still
624 // remove the backing file if we want.
625 loop->Detach(pieces[1]);
626 } else {
627 LOG(ERROR) << "Unknown status: " << pieces[0];
628 }
629 }
630
631 std::string message;
632 if (!android::base::RemoveFileIfExists(status_file, &message)) {
633 LOG(ERROR) << "Could not remove " << status_file << ": " << message;
634 }
635
636 auto status_prop = GetStatusPropertyName(name);
637 android::base::SetProperty(status_prop, "");
638 return true;
639 }
640
RemoveAllImages()641 bool ImageManager::RemoveAllImages() {
642 if (!MetadataExists(metadata_dir_)) {
643 return true;
644 }
645 auto metadata = OpenMetadata(metadata_dir_);
646 if (!metadata) {
647 return RemoveAllMetadata(metadata_dir_);
648 }
649
650 bool ok = true;
651 for (const auto& partition : metadata->partitions) {
652 auto partition_name = GetPartitionName(partition);
653 ok &= DeleteBackingImage(partition_name);
654 }
655 return ok && RemoveAllMetadata(metadata_dir_);
656 }
657
Validate()658 bool ImageManager::Validate() {
659 auto metadata = OpenMetadata(metadata_dir_);
660 if (!metadata) {
661 return false;
662 }
663
664 bool ok = true;
665 for (const auto& partition : metadata->partitions) {
666 auto name = GetPartitionName(partition);
667 auto image_path = GetImageHeaderPath(name);
668 auto fiemap = SplitFiemap::Open(image_path);
669 if (fiemap == nullptr) {
670 LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
671 ok = false;
672 continue;
673 }
674 if (!fiemap->HasPinnedExtents()) {
675 LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
676 ok = false;
677 }
678 }
679 return ok;
680 }
681
DisableImage(const std::string & name)682 bool ImageManager::DisableImage(const std::string& name) {
683 return AddAttributes(metadata_dir_, name, LP_PARTITION_ATTR_DISABLED);
684 }
685
RemoveDisabledImages()686 bool ImageManager::RemoveDisabledImages() {
687 if (!MetadataExists(metadata_dir_)) {
688 return true;
689 }
690
691 auto metadata = OpenMetadata(metadata_dir_);
692 if (!metadata) {
693 return false;
694 }
695
696 bool ok = true;
697 for (const auto& partition : metadata->partitions) {
698 if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
699 ok &= DeleteBackingImage(GetPartitionName(partition));
700 }
701 }
702 return ok;
703 }
704
GetMappedImageDevice(const std::string & name,std::string * device)705 bool ImageManager::GetMappedImageDevice(const std::string& name, std::string* device) {
706 auto prop_name = GetStatusPropertyName(name);
707 *device = android::base::GetProperty(prop_name, "");
708 if (!device->empty()) {
709 return true;
710 }
711
712 auto& dm = DeviceMapper::Instance();
713 if (dm.GetState(name) == DmDeviceState::INVALID) {
714 return false;
715 }
716 return dm.GetDmDevicePathByName(name, device);
717 }
718
MapAllImages(const std::function<bool (std::set<std::string>)> & init)719 bool ImageManager::MapAllImages(const std::function<bool(std::set<std::string>)>& init) {
720 if (!MetadataExists(metadata_dir_)) {
721 return true;
722 }
723
724 auto metadata = OpenMetadata(metadata_dir_);
725 if (!metadata) {
726 return false;
727 }
728
729 std::set<std::string> devices;
730 for (const auto& name : GetBlockDevicePartitionNames(*metadata.get())) {
731 devices.emplace(name);
732 }
733 if (!init(std::move(devices))) {
734 return false;
735 }
736
737 auto data_device = GetMetadataSuperBlockDevice(*metadata.get());
738 auto data_partition_name = GetBlockDevicePartitionName(*data_device);
739 return CreateLogicalPartitions(*metadata.get(), data_partition_name);
740 }
741
Open(IImageManager * manager,const std::chrono::milliseconds & timeout_ms,const std::string & name)742 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
743 const std::chrono::milliseconds& timeout_ms,
744 const std::string& name) {
745 std::string path;
746 if (!manager->MapImageDevice(name, timeout_ms, &path)) {
747 return nullptr;
748 }
749
750 auto device = std::unique_ptr<MappedDevice>(new MappedDevice(manager, name, path));
751 if (device->fd() < 0) {
752 return nullptr;
753 }
754 return device;
755 }
756
MappedDevice(IImageManager * manager,const std::string & name,const std::string & path)757 MappedDevice::MappedDevice(IImageManager* manager, const std::string& name, const std::string& path)
758 : manager_(manager), name_(name), path_(path) {
759 // The device is already mapped; try and open it.
760 fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
761 }
762
~MappedDevice()763 MappedDevice::~MappedDevice() {
764 fd_ = {};
765 manager_->UnmapImageDevice(name_);
766 }
767
UnmapImageIfExists(const std::string & name)768 bool IImageManager::UnmapImageIfExists(const std::string& name) {
769 // No lock is needed even though this seems to be vulnerable to TOCTOU. If process A
770 // calls MapImageDevice() while process B calls UnmapImageIfExists(), and MapImageDevice()
771 // happens after process B checks IsImageMapped(), it would be as if MapImageDevice() is called
772 // after process B finishes calling UnmapImageIfExists(), resulting the image to be mapped,
773 // which is a reasonable sequence.
774 if (!IsImageMapped(name)) {
775 return true;
776 }
777 return UnmapImageDevice(name);
778 }
779
780 } // namespace fiemap
781 } // namespace android
782