Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21394f6e authored by David Anderson's avatar David Anderson Committed by Gerrit Code Review
Browse files

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

parents e32697ee 671bd812
Loading
Loading
Loading
Loading
+85 −0
Original line number Diff line number Diff line
@@ -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;