Loading fs_mgr/fs_mgr.cpp +85 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ using android::base::Timer; using android::base::unique_fd; using android::dm::DeviceMapper; using android::dm::DmDeviceState; using android::dm::DmTargetLinear; // Realistically, this file should be part of the android::fs_mgr namespace; using namespace android::fs_mgr; Loading Loading @@ -1114,6 +1115,83 @@ std::string fs_mgr_find_bow_device(const std::string& block_device) { } } static constexpr const char* kUserdataWrapperName = "userdata-wrapper"; static void WrapUserdata(FstabEntry* entry, dev_t dev, const std::string& block_device) { DeviceMapper& dm = DeviceMapper::Instance(); if (dm.GetState(kUserdataWrapperName) != DmDeviceState::INVALID) { // This will report failure for us. If we do fail to get the path, // we leave the device unwrapped. dm.GetDmDevicePathByName(kUserdataWrapperName, &entry->blk_device); return; } unique_fd fd(open(block_device.c_str(), O_RDONLY | O_CLOEXEC)); if (fd < 0) { PLOG(ERROR) << "open failed: " << entry->blk_device; return; } auto dev_str = android::base::StringPrintf("%u:%u", major(dev), minor(dev)); uint64_t sectors = get_block_device_size(fd) / 512; android::dm::DmTable table; table.Emplace<DmTargetLinear>(0, sectors, dev_str, 0); std::string dm_path; if (!dm.CreateDevice(kUserdataWrapperName, table, &dm_path, 20s)) { LOG(ERROR) << "Failed to create userdata wrapper device"; return; } entry->blk_device = dm_path; } // When using Virtual A/B, partitions can be backed by /data and mapped with // device-mapper in first-stage init. This can happen when merging an OTA or // when using adb remount to house "scratch". In this case, /data cannot be // mounted directly off the userdata block device, and e2fsck will refuse to // scan it, because the kernel reports the block device as in-use. // // As a workaround, when mounting /data, we create a trivial dm-linear wrapper // if the underlying block device already has dependencies. Note that we make // an exception for metadata-encrypted devices, since dm-default-key is already // a wrapper. static void WrapUserdataIfNeeded(FstabEntry* entry, const std::string& actual_block_device = {}) { const auto& block_device = actual_block_device.empty() ? entry->blk_device : actual_block_device; if (entry->mount_point != "/data" || !entry->key_dir.empty() || android::base::StartsWith(block_device, "/dev/block/dm-")) { return; } struct stat st; if (stat(block_device.c_str(), &st) < 0) { PLOG(ERROR) << "stat failed: " << block_device; return; } std::string path = android::base::StringPrintf("/sys/dev/block/%u:%u/holders", major(st.st_rdev), minor(st.st_rdev)); std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir); if (!dir) { PLOG(ERROR) << "opendir failed: " << path; return; } struct dirent* d; bool has_holders = false; while ((d = readdir(dir.get())) != nullptr) { if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) { has_holders = true; break; } } if (has_holders) { WrapUserdata(entry, st.st_rdev, block_device); } } static bool IsMountPointMounted(const std::string& mount_point) { // Check if this is already mounted. Fstab fstab; Loading Loading @@ -1193,6 +1271,8 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) { } } WrapUserdataIfNeeded(¤t_entry); if (!checkpoint_manager.Update(¤t_entry)) { continue; } Loading Loading @@ -1463,6 +1543,9 @@ int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) { } std::string block_device; if (auto entry = GetEntryForMountPoint(&proc_mounts, "/data"); entry != nullptr) { // Note: we don't care about a userdata wrapper here, since it's safe // to remount on top of the bow device instead, there will be no // conflicts. block_device = entry->blk_device; } else { LERROR << "/data is not mounted"; Loading Loading @@ -1581,6 +1664,8 @@ static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name, } } WrapUserdataIfNeeded(&fstab_entry, n_blk_device); if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) { LERROR << "Could not set up checkpoint partition, skipping!"; continue; Loading Loading
fs_mgr/fs_mgr.cpp +85 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ using android::base::Timer; using android::base::unique_fd; using android::dm::DeviceMapper; using android::dm::DmDeviceState; using android::dm::DmTargetLinear; // Realistically, this file should be part of the android::fs_mgr namespace; using namespace android::fs_mgr; Loading Loading @@ -1114,6 +1115,83 @@ std::string fs_mgr_find_bow_device(const std::string& block_device) { } } static constexpr const char* kUserdataWrapperName = "userdata-wrapper"; static void WrapUserdata(FstabEntry* entry, dev_t dev, const std::string& block_device) { DeviceMapper& dm = DeviceMapper::Instance(); if (dm.GetState(kUserdataWrapperName) != DmDeviceState::INVALID) { // This will report failure for us. If we do fail to get the path, // we leave the device unwrapped. dm.GetDmDevicePathByName(kUserdataWrapperName, &entry->blk_device); return; } unique_fd fd(open(block_device.c_str(), O_RDONLY | O_CLOEXEC)); if (fd < 0) { PLOG(ERROR) << "open failed: " << entry->blk_device; return; } auto dev_str = android::base::StringPrintf("%u:%u", major(dev), minor(dev)); uint64_t sectors = get_block_device_size(fd) / 512; android::dm::DmTable table; table.Emplace<DmTargetLinear>(0, sectors, dev_str, 0); std::string dm_path; if (!dm.CreateDevice(kUserdataWrapperName, table, &dm_path, 20s)) { LOG(ERROR) << "Failed to create userdata wrapper device"; return; } entry->blk_device = dm_path; } // When using Virtual A/B, partitions can be backed by /data and mapped with // device-mapper in first-stage init. This can happen when merging an OTA or // when using adb remount to house "scratch". In this case, /data cannot be // mounted directly off the userdata block device, and e2fsck will refuse to // scan it, because the kernel reports the block device as in-use. // // As a workaround, when mounting /data, we create a trivial dm-linear wrapper // if the underlying block device already has dependencies. Note that we make // an exception for metadata-encrypted devices, since dm-default-key is already // a wrapper. static void WrapUserdataIfNeeded(FstabEntry* entry, const std::string& actual_block_device = {}) { const auto& block_device = actual_block_device.empty() ? entry->blk_device : actual_block_device; if (entry->mount_point != "/data" || !entry->key_dir.empty() || android::base::StartsWith(block_device, "/dev/block/dm-")) { return; } struct stat st; if (stat(block_device.c_str(), &st) < 0) { PLOG(ERROR) << "stat failed: " << block_device; return; } std::string path = android::base::StringPrintf("/sys/dev/block/%u:%u/holders", major(st.st_rdev), minor(st.st_rdev)); std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir); if (!dir) { PLOG(ERROR) << "opendir failed: " << path; return; } struct dirent* d; bool has_holders = false; while ((d = readdir(dir.get())) != nullptr) { if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) { has_holders = true; break; } } if (has_holders) { WrapUserdata(entry, st.st_rdev, block_device); } } static bool IsMountPointMounted(const std::string& mount_point) { // Check if this is already mounted. Fstab fstab; Loading Loading @@ -1193,6 +1271,8 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) { } } WrapUserdataIfNeeded(¤t_entry); if (!checkpoint_manager.Update(¤t_entry)) { continue; } Loading Loading @@ -1463,6 +1543,9 @@ int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) { } std::string block_device; if (auto entry = GetEntryForMountPoint(&proc_mounts, "/data"); entry != nullptr) { // Note: we don't care about a userdata wrapper here, since it's safe // to remount on top of the bow device instead, there will be no // conflicts. block_device = entry->blk_device; } else { LERROR << "/data is not mounted"; Loading Loading @@ -1581,6 +1664,8 @@ static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name, } } WrapUserdataIfNeeded(&fstab_entry, n_blk_device); if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) { LERROR << "Could not set up checkpoint partition, skipping!"; continue; Loading