From 323959ea2797fbca23f3ce454de7d2597728dc39 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Tue, 25 Jun 2019 14:36:52 -0700 Subject: [PATCH] Make ext4 userdata checkpoints work with metadata encryption When both ext4 user data checkpoints and metadata encryption are enabled, we are creating two stacked dm devices. This had not been properly thought through or debugged. Test: Enable metadata encryption on taimen (add keydirectory=/metadata/vold/metadata_encryption to flags for userdata in fstab.hardware) Unfortunately metadata is not wiped by fastboot -w, so it is necessary to rm metadata/vold -rf whenever you wipe data. fastboot flashall -w works fastboot reboot -w works A normal boot works Disable checkpoint commits with setprop persist.vold.dont_commit_checkpoint 1 vdc checkpoint startCheckpoint 10 adb reboot wait for device to fully boot then adb reboot Wait for device to fully boot then adb logcat -d | grep Checkpoint shows the rollback in the logs This tests encryption on top of checkpoints with commit, encryption without checkpoints, and rollback, which seems to be the key cases. Bug: 135905679 Change-Id: I24387a2943dce28b918c34894f24911b20429be7 --- fs_mgr/fs_mgr.cpp | 118 ++++++++++++++++++++++++++++------------ fs_mgr/include/fs_mgr.h | 4 ++ 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index 2a9a9d022..259f80076 100644 --- a/fs_mgr/fs_mgr.cpp +++ b/fs_mgr/fs_mgr.cpp @@ -908,7 +908,7 @@ class CheckpointManager { public: CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {} - bool Update(FstabEntry* entry) { + bool Update(FstabEntry* entry, const std::string& block_device = std::string()) { if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) { return true; } @@ -927,7 +927,7 @@ class CheckpointManager { return true; } - if (!UpdateCheckpointPartition(entry)) { + if (!UpdateCheckpointPartition(entry, block_device)) { LERROR << "Could not set up checkpoint partition, skipping!"; return false; } @@ -957,7 +957,7 @@ class CheckpointManager { } private: - bool UpdateCheckpointPartition(FstabEntry* entry) { + bool UpdateCheckpointPartition(FstabEntry* entry, const std::string& block_device) { if (entry->fs_mgr_flags.checkpoint_fs) { if (is_f2fs(entry->fs_type)) { entry->fs_options += ",checkpoint=disable"; @@ -965,39 +965,43 @@ class CheckpointManager { LERROR << entry->fs_type << " does not implement checkpoints."; } } else if (entry->fs_mgr_flags.checkpoint_blk) { - unique_fd fd(TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC))); - if (fd < 0) { - PERROR << "Cannot open device " << entry->blk_device; - return false; - } + auto actual_block_device = block_device.empty() ? entry->blk_device : block_device; + if (fs_mgr_find_bow_device(actual_block_device).empty()) { + unique_fd fd( + TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC))); + if (fd < 0) { + PERROR << "Cannot open device " << entry->blk_device; + return false; + } - uint64_t size = get_block_device_size(fd) / 512; - if (!size) { - PERROR << "Cannot get device size"; - return false; - } + uint64_t size = get_block_device_size(fd) / 512; + if (!size) { + PERROR << "Cannot get device size"; + return false; + } - android::dm::DmTable table; - if (!table.AddTarget( - std::make_unique(0, size, entry->blk_device))) { - LERROR << "Failed to add bow target"; - return false; - } + android::dm::DmTable table; + if (!table.AddTarget(std::make_unique( + 0, size, entry->blk_device))) { + LERROR << "Failed to add bow target"; + return false; + } - DeviceMapper& dm = DeviceMapper::Instance(); - if (!dm.CreateDevice("bow", table)) { - PERROR << "Failed to create bow device"; - return false; - } + DeviceMapper& dm = DeviceMapper::Instance(); + if (!dm.CreateDevice("bow", table)) { + PERROR << "Failed to create bow device"; + return false; + } - std::string name; - if (!dm.GetDmDevicePathByName("bow", &name)) { - PERROR << "Failed to get bow device name"; - return false; - } + std::string name; + if (!dm.GetDmDevicePathByName("bow", &name)) { + PERROR << "Failed to get bow device name"; + return false; + } - device_map_[name] = entry->blk_device; - entry->blk_device = name; + device_map_[name] = entry->blk_device; + entry->blk_device = name; + } } return true; } @@ -1007,6 +1011,50 @@ class CheckpointManager { std::map device_map_; }; +std::string fs_mgr_find_bow_device(const std::string& block_device) { + if (block_device.substr(0, 5) != "/dev/") { + LOG(ERROR) << "Expected block device, got " << block_device; + return std::string(); + } + + std::string sys_dir = std::string("/sys/") + block_device.substr(5); + + for (;;) { + std::string name; + if (!android::base::ReadFileToString(sys_dir + "/dm/name", &name)) { + PLOG(ERROR) << block_device << " is not dm device"; + return std::string(); + } + + if (name == "bow\n") return sys_dir; + + std::string slaves = sys_dir + "/slaves"; + std::unique_ptr directory(opendir(slaves.c_str()), closedir); + if (!directory) { + PLOG(ERROR) << "Can't open slave directory " << slaves; + return std::string(); + } + + int count = 0; + for (dirent* entry = readdir(directory.get()); entry; entry = readdir(directory.get())) { + if (entry->d_type != DT_LNK) continue; + + if (count == 1) { + LOG(ERROR) << "Too many slaves in " << slaves; + return std::string(); + } + + ++count; + sys_dir = std::string("/sys/block/") + entry->d_name; + } + + if (count != 1) { + LOG(ERROR) << "No slave in " << slaves; + return std::string(); + } + } +} + static bool IsMountPointMounted(const std::string& mount_point) { // Check if this is already mounted. Fstab fstab; @@ -1144,7 +1192,8 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) { } encryptable = status; if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) { - if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.mount_point})) { + if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device, + attempted_entry.mount_point})) { LERROR << "Encryption failed"; return FS_MGR_MNTALL_FAIL; } @@ -1215,7 +1264,8 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) { encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; } else if (mount_errno != EBUSY && mount_errno != EACCES && should_use_metadata_encryption(attempted_entry)) { - if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.mount_point})) { + if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device, + attempted_entry.mount_point})) { ++error_count; } encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED; @@ -1345,7 +1395,7 @@ static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name, } } - if (!checkpoint_manager.Update(&fstab_entry)) { + if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) { LERROR << "Could not set up checkpoint partition, skipping!"; continue; } diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 88b2f8f4f..bdec7bee5 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -104,3 +104,7 @@ enum FsMgrUmountStatus : int { // fs_mgr_umount_all() is the reverse of fs_mgr_mount_all. In particular, // it destroys verity devices from device mapper after the device is unmounted. int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab); + +// Finds the dm_bow device on which this block device is stacked, or returns +// empty string +std::string fs_mgr_find_bow_device(const std::string& block_device);