• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_RAMDISK__
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 #ifndef __ANDROID_RAMDISK__
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 we COULD
535     // perform normal writes to /data/gsi (which is unencrypted), but given that
536     // metadata encryption has been mandated since Android R, we don't actually
537     // support or test this.
538     //
539     // So, we validate here that /data is backed by device-mapper. This code
540     // isn't needed in recovery since there is no /data.
541     //
542     // If this logic sticks for a release, we can remove MapWithLoopDevice, as
543     // well as WrapUserdataIfNeeded in fs_mgr.
544     std::string block_device;
545     bool can_use_devicemapper;
546     if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) {
547         LOG(ERROR) << "Could not determine block device for " << image_header;
548         return false;
549     }
550 
551     if (!can_use_devicemapper) {
552         LOG(ERROR) << "Cannot map image: /data must be mounted on top of device-mapper.";
553         return false;
554     }
555 #endif
556 
557     if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
558         return false;
559     }
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 
DisableAllImages()658 bool ImageManager::DisableAllImages() {
659     if (!MetadataExists(metadata_dir_)) {
660         return true;
661     }
662     auto metadata = OpenMetadata(metadata_dir_);
663     if (!metadata) {
664         return false;
665     }
666 
667     bool ok = true;
668     for (const auto& partition : metadata->partitions) {
669         auto partition_name = GetPartitionName(partition);
670         ok &= DisableImage(partition_name);
671     }
672     return ok;
673 }
674 
Validate()675 bool ImageManager::Validate() {
676     auto metadata = OpenMetadata(metadata_dir_);
677     if (!metadata) {
678         return false;
679     }
680 
681     bool ok = true;
682     for (const auto& partition : metadata->partitions) {
683         auto name = GetPartitionName(partition);
684         auto image_path = GetImageHeaderPath(name);
685         auto fiemap = SplitFiemap::Open(image_path);
686         if (fiemap == nullptr) {
687             LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
688             ok = false;
689             continue;
690         }
691         if (!fiemap->HasPinnedExtents()) {
692             LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
693             ok = false;
694         }
695     }
696     return ok;
697 }
698 
DisableImage(const std::string & name)699 bool ImageManager::DisableImage(const std::string& name) {
700     return AddAttributes(metadata_dir_, name, LP_PARTITION_ATTR_DISABLED);
701 }
702 
RemoveDisabledImages()703 bool ImageManager::RemoveDisabledImages() {
704     if (!MetadataExists(metadata_dir_)) {
705         return true;
706     }
707 
708     auto metadata = OpenMetadata(metadata_dir_);
709     if (!metadata) {
710         return false;
711     }
712 
713     bool ok = true;
714     for (const auto& partition : metadata->partitions) {
715         if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
716             const auto name = GetPartitionName(partition);
717             if (!DeleteBackingImage(name)) {
718                 ok = false;
719             } else {
720                 LOG(INFO) << "Removed disabled partition image: " << name;
721             }
722         }
723     }
724     return ok;
725 }
726 
GetMappedImageDevice(const std::string & name,std::string * device)727 bool ImageManager::GetMappedImageDevice(const std::string& name, std::string* device) {
728     auto prop_name = GetStatusPropertyName(name);
729     *device = android::base::GetProperty(prop_name, "");
730     if (!device->empty()) {
731         return true;
732     }
733 
734     auto& dm = DeviceMapper::Instance();
735     if (dm.GetState(name) == DmDeviceState::INVALID) {
736         return false;
737     }
738     return dm.GetDmDevicePathByName(name, device);
739 }
740 
MapAllImages(const std::function<bool (std::set<std::string>)> & init)741 bool ImageManager::MapAllImages(const std::function<bool(std::set<std::string>)>& init) {
742     if (!MetadataExists(metadata_dir_)) {
743         return true;
744     }
745 
746     auto metadata = OpenMetadata(metadata_dir_);
747     if (!metadata) {
748         return false;
749     }
750 
751     std::set<std::string> devices;
752     for (const auto& name : GetBlockDevicePartitionNames(*metadata.get())) {
753         devices.emplace(name);
754     }
755     if (!init(std::move(devices))) {
756         return false;
757     }
758 
759     auto data_device = GetMetadataSuperBlockDevice(*metadata.get());
760     auto data_partition_name = GetBlockDevicePartitionName(*data_device);
761     return CreateLogicalPartitions(*metadata.get(), data_partition_name);
762 }
763 
operator <<(std::ostream & os,android::fs_mgr::Extent * extent)764 std::ostream& operator<<(std::ostream& os, android::fs_mgr::Extent* extent) {
765     if (auto e = extent->AsLinearExtent()) {
766         return os << "<begin:" << e->physical_sector() << ", end:" << e->end_sector()
767                   << ", device:" << e->device_index() << ">";
768     }
769     return os << "<unknown>";
770 }
771 
CompareExtent(android::fs_mgr::Extent * a,android::fs_mgr::Extent * b)772 static bool CompareExtent(android::fs_mgr::Extent* a, android::fs_mgr::Extent* b) {
773     if (auto linear_a = a->AsLinearExtent()) {
774         auto linear_b = b->AsLinearExtent();
775         if (!linear_b) {
776             return false;
777         }
778         return linear_a->physical_sector() == linear_b->physical_sector() &&
779                linear_a->num_sectors() == linear_b->num_sectors() &&
780                linear_a->device_index() == linear_b->device_index();
781     }
782     return false;
783 }
784 
CompareExtents(android::fs_mgr::Partition * oldp,android::fs_mgr::Partition * newp)785 static bool CompareExtents(android::fs_mgr::Partition* oldp, android::fs_mgr::Partition* newp) {
786     const auto& old_extents = oldp->extents();
787     const auto& new_extents = newp->extents();
788 
789     auto old_iter = old_extents.begin();
790     auto new_iter = new_extents.begin();
791     while (true) {
792         if (old_iter == old_extents.end()) {
793             if (new_iter == new_extents.end()) {
794                 break;
795             }
796             LOG(ERROR) << "Unexpected extent added: " << (*new_iter);
797             return false;
798         }
799         if (new_iter == new_extents.end()) {
800             LOG(ERROR) << "Unexpected extent removed: " << (*old_iter);
801             return false;
802         }
803 
804         if (!CompareExtent(old_iter->get(), new_iter->get())) {
805             LOG(ERROR) << "Extents do not match: " << old_iter->get() << ", " << new_iter->get();
806             return false;
807         }
808 
809         old_iter++;
810         new_iter++;
811     }
812     return true;
813 }
814 
ValidateImageMaps()815 bool ImageManager::ValidateImageMaps() {
816     if (!MetadataExists(metadata_dir_)) {
817         LOG(INFO) << "ImageManager skipping verification; no images for " << metadata_dir_;
818         return true;
819     }
820 
821     auto metadata = OpenMetadata(metadata_dir_);
822     if (!metadata) {
823         LOG(ERROR) << "ImageManager skipping verification; failed to open " << metadata_dir_;
824         return true;
825     }
826 
827     for (const auto& partition : metadata->partitions) {
828         auto name = GetPartitionName(partition);
829         auto image_path = GetImageHeaderPath(name);
830         auto fiemap = SplitFiemap::Open(image_path);
831         if (fiemap == nullptr) {
832             LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
833             return false;
834         }
835         if (!fiemap->HasPinnedExtents()) {
836             LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
837             return false;
838         }
839 
840         android::fs_mgr::PartitionOpener opener;
841         auto builder = android::fs_mgr::MetadataBuilder::New(*metadata.get(), &opener);
842         if (!builder) {
843             LOG(ERROR) << "Could not create metadata builder: " << image_path;
844             return false;
845         }
846 
847         auto new_p = builder->AddPartition("_temp_for_verify", 0);
848         if (!new_p) {
849             LOG(ERROR) << "Could not add temporary partition: " << image_path;
850             return false;
851         }
852 
853         auto partition_size = android::fs_mgr::GetPartitionSize(*metadata.get(), partition);
854         if (!FillPartitionExtents(builder.get(), new_p, fiemap.get(), partition_size)) {
855             LOG(ERROR) << "Could not fill partition extents: " << image_path;
856             return false;
857         }
858 
859         auto old_p = builder->FindPartition(name);
860         if (!old_p) {
861             LOG(ERROR) << "Could not find metadata for " << image_path;
862             return false;
863         }
864 
865         if (!CompareExtents(old_p, new_p)) {
866             LOG(ERROR) << "Metadata for " << image_path << " does not match fiemap";
867             return false;
868         }
869     }
870 
871     return true;
872 }
873 
IsImageDisabled(const std::string & name)874 bool ImageManager::IsImageDisabled(const std::string& name) {
875     if (!MetadataExists(metadata_dir_)) {
876         return true;
877     }
878 
879     auto metadata = OpenMetadata(metadata_dir_);
880     if (!metadata) {
881         return false;
882     }
883 
884     auto partition = FindPartition(*metadata.get(), name);
885     if (!partition) {
886         return false;
887     }
888 
889     return !!(partition->attributes & LP_PARTITION_ATTR_DISABLED);
890 }
891 
Open(IImageManager * manager,const std::chrono::milliseconds & timeout_ms,const std::string & name)892 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
893                                                  const std::chrono::milliseconds& timeout_ms,
894                                                  const std::string& name) {
895     std::string path;
896     if (!manager->MapImageDevice(name, timeout_ms, &path)) {
897         return nullptr;
898     }
899 
900     auto device = std::unique_ptr<MappedDevice>(new MappedDevice(manager, name, path));
901     if (device->fd() < 0) {
902         return nullptr;
903     }
904     return device;
905 }
906 
MappedDevice(IImageManager * manager,const std::string & name,const std::string & path)907 MappedDevice::MappedDevice(IImageManager* manager, const std::string& name, const std::string& path)
908     : manager_(manager), name_(name), path_(path) {
909     // The device is already mapped; try and open it.
910     fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
911 }
912 
~MappedDevice()913 MappedDevice::~MappedDevice() {
914     fd_ = {};
915     manager_->UnmapImageDevice(name_);
916 }
917 
UnmapImageIfExists(const std::string & name)918 bool IImageManager::UnmapImageIfExists(const std::string& name) {
919     // No lock is needed even though this seems to be vulnerable to TOCTOU. If process A
920     // calls MapImageDevice() while process B calls UnmapImageIfExists(), and MapImageDevice()
921     // happens after process B checks IsImageMapped(), it would be as if MapImageDevice() is called
922     // after process B finishes calling UnmapImageIfExists(), resulting the image to be mapped,
923     // which is a reasonable sequence.
924     if (!IsImageMapped(name)) {
925         return true;
926     }
927     return UnmapImageDevice(name);
928 }
929 
930 }  // namespace fiemap
931 }  // namespace android
932