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