Merge "Fix mounting /data on Virtual A/B devices without metadata encryption."

am: 21394f6e85

Change-Id: I6b413b57485f56cc31091a03dddbd78be4239fa3
This commit is contained in:
David Anderson 2020-01-27 09:10:29 -08:00 committed by android-build-merger
commit 3f52b42db0

View file

@ -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;
@ -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;
@ -1193,6 +1271,8 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
}
}
WrapUserdataIfNeeded(&current_entry);
if (!checkpoint_manager.Update(&current_entry)) {
continue;
}
@ -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";
@ -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;