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

Commit 5110628a authored by Nikita Ioffe's avatar Nikita Ioffe
Browse files

Fix logic to figure out what /data fstab entry was mounted

Now the logic is handled by following API:
GetMountedEntryForUserdata(Fstab* fstab).

Behind the scenes it does the following:
1. Reads /proc/mounts and gets block device /data is actually mounted on.
2. In case of it's a dm-device, recursively goes into it's slaves until
finds the underlying block device.
3. Optimistically assumes that corresponding block device is a symlink
and tries to read it.
4. Reads all the entries corresponding to /data from fstab.
5. For each of them, optimistically tries to readlink block_device.
6. If it matches the resolved block devices, we found our fstab entry!

Also added a test to CtsFsMgrTestCases asserting that /data was mounted
from one of entries in default fstab.

Test: on blueline & taimen, with & without checkpointing:
Test: atest CtsFsMgrTestCases
Test: adb reboot userspace

Bug: 135984674
Change-Id: Ic70daeeb18096c7b134004334cc674dacc6e36f3
parent 40239389
Loading
Loading
Loading
Loading
+2 −31
Original line number Diff line number Diff line
@@ -1351,38 +1351,9 @@ int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) {
    return ret;
}

static std::string GetUserdataBlockDevice() {
    Fstab fstab;
    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
        LERROR << "Failed to read /proc/mounts";
        return "";
    }
    auto entry = GetEntryForMountPoint(&fstab, "/data");
    if (entry == nullptr) {
        LERROR << "Didn't find /data mount point in /proc/mounts";
        return "";
    }
    return entry->blk_device;
}

int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
    const std::string& block_device = GetUserdataBlockDevice();
    LINFO << "Userdata is mounted on " << block_device;
    auto entry = std::find_if(fstab->begin(), fstab->end(), [&block_device](const FstabEntry& e) {
        if (e.mount_point != "/data") {
            return false;
        }
        if (e.blk_device == block_device) {
            return true;
        }
        DeviceMapper& dm = DeviceMapper::Instance();
        std::string path;
        if (!dm.GetDmDevicePathByName("userdata", &path)) {
            return false;
        }
        return path == block_device;
    });
    if (entry == fstab->end()) {
    auto entry = GetMountedEntryForUserdata(fstab);
    if (entry == nullptr) {
        LERROR << "Can't find /data in fstab";
        return -1;
    }
+84 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ using android::base::EndsWith;
using android::base::ParseByteCount;
using android::base::ParseInt;
using android::base::ReadFileToString;
using android::base::Readlink;
using android::base::Split;
using android::base::StartsWith;

@@ -809,6 +810,89 @@ std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string
    return entries;
}

static std::string ResolveBlockDevice(const std::string& block_device) {
    if (!StartsWith(block_device, "/dev/block/")) {
        LWARNING << block_device << " is not a block device";
        return block_device;
    }
    std::string name = block_device.substr(5);
    if (!StartsWith(name, "block/dm-")) {
        // Not a dm-device, but might be a symlink. Optimistically try to readlink.
        std::string result;
        if (Readlink(block_device, &result)) {
            return result;
        } else if (errno == EINVAL) {
            // After all, it wasn't a symlink.
            return block_device;
        } else {
            LERROR << "Failed to readlink " << block_device;
            return "";
        }
    }
    // It's a dm-device, let's find what's inside!
    std::string sys_dir = "/sys/" + name;
    while (true) {
        std::string slaves_dir = sys_dir + "/slaves";
        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(slaves_dir.c_str()), closedir);
        if (!dir) {
            LERROR << "Failed to open " << slaves_dir;
            return "";
        }
        std::string sub_device_name = "";
        for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
            if (entry->d_type != DT_LNK) continue;
            if (!sub_device_name.empty()) {
                LERROR << "Too many slaves in " << slaves_dir;
                return "";
            }
            sub_device_name = entry->d_name;
        }
        if (sub_device_name.empty()) {
            LERROR << "No slaves in " << slaves_dir;
            return "";
        }
        if (!StartsWith(sub_device_name, "dm-")) {
            // Not a dm-device! We can stop now.
            return "/dev/block/" + sub_device_name;
        }
        // Still a dm-device, keep digging.
        sys_dir = "/sys/block/" + sub_device_name;
    }
}

FstabEntry* GetMountedEntryForUserdata(Fstab* fstab) {
    Fstab mounts;
    if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
        LERROR << "Failed to read /proc/mounts";
        return nullptr;
    }
    auto mounted_entry = GetEntryForMountPoint(&mounts, "/data");
    if (mounted_entry == nullptr) {
        LWARNING << "/data is not mounted";
        return nullptr;
    }
    std::string resolved_block_device = ResolveBlockDevice(mounted_entry->blk_device);
    if (resolved_block_device.empty()) {
        return nullptr;
    }
    LINFO << "/data is mounted on " << resolved_block_device;
    for (auto& entry : *fstab) {
        if (entry.mount_point != "/data") {
            continue;
        }
        std::string block_device;
        if (!Readlink(entry.blk_device, &block_device)) {
            LWARNING << "Failed to readlink " << entry.blk_device;
            block_device = entry.blk_device;
        }
        if (block_device == resolved_block_device) {
            return &entry;
        }
    }
    LERROR << "Didn't find entry that was used to mount /data";
    return nullptr;
}

std::set<std::string> GetBootDevices() {
    // First check the kernel commandline, then try the device tree otherwise
    std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
+1 −0
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ bool SkipMountingPartitions(Fstab* fstab);
FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
// The Fstab can contain multiple entries for the same mount point with different configurations.
std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
FstabEntry* GetMountedEntryForUserdata(Fstab* fstab);

// This method builds DSU fstab entries and transfer the fstab.
//
+11 −0
Original line number Diff line number Diff line
@@ -969,3 +969,14 @@ TEST(fs_mgr, DefaultFstabContainsUserdata) {
    ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, "/data"))
            << "Default fstab doesn't contain /data entry";
}

TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
    if (getuid() != 0) {
        GTEST_SKIP() << "Must be run as root.";
        return;
    }
    Fstab fstab;
    ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
    ASSERT_NE(nullptr, GetMountedEntryForUserdata(&fstab))
            << "/data wasn't mounted from default fstab";
}