Merge "Fix mounting /data on Virtual A/B devices without metadata encryption."
am: 21394f6e85
Change-Id: I6b413b57485f56cc31091a03dddbd78be4239fa3
This commit is contained in:
commit
3f52b42db0
1 changed files with 85 additions and 0 deletions
|
@ -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(¤t_entry);
|
||||
|
||||
if (!checkpoint_manager.Update(¤t_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;
|
||||
|
|
Loading…
Reference in a new issue