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