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

Commit ccf0d393 authored by Tom Cherry's avatar Tom Cherry
Browse files

init: poll in first stage mount if required devices are not found

First stage mount in init currently attempts to regenerate uevents for
specific devices to create the corresponding dev nodes.  However, this
is racy as first stage mount happens early in the boot process and
it's possible that some of these devices have not yet been created by
the kernel.

To fix this issue, init will poll on the uevent socket for up to 10
seconds waiting for the kernel to create the required device.  It will
return false and panic if this 10 second timeout passes.

Note that the same uevent socket is used in the uevent regeneration
and the polling code, so there is no race if the device is created
after the uevent regeneration and before polling starts; the first
poll will pick up the device.

Bug: 62681642
Bug: 62682821
Test: Boot bullhead
Test: Boot sailfish
Test: Boot hikey + hotplug/unplug sdcard
Change-Id: I4a6ff043eb7115b729ca4954ebc6c9e000132993
parent 137ee511
Loading
Loading
Loading
Loading
+73 −47
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <stdlib.h>
#include <unistd.h>
#include <unistd.h>


#include <chrono>
#include <memory>
#include <memory>
#include <set>
#include <set>
#include <string>
#include <string>
@@ -35,6 +36,8 @@
#include "uevent_listener.h"
#include "uevent_listener.h"
#include "util.h"
#include "util.h"


using namespace std::chrono_literals;

// Class Declarations
// Class Declarations
// ------------------
// ------------------
class FirstStageMount {
class FirstStageMount {
@@ -49,11 +52,11 @@ class FirstStageMount {
    bool InitDevices();
    bool InitDevices();


  protected:
  protected:
    void InitRequiredDevices();
    bool InitRequiredDevices();
    void InitVerityDevice(const std::string& verity_device);
    bool InitVerityDevice(const std::string& verity_device);
    bool MountPartitions();
    bool MountPartitions();


    virtual RegenerationAction UeventCallback(const Uevent& uevent);
    virtual ListenerAction UeventCallback(const Uevent& uevent);


    // Pure virtual functions.
    // Pure virtual functions.
    virtual bool GetRequiredDevices() = 0;
    virtual bool GetRequiredDevices() = 0;
@@ -86,7 +89,7 @@ class FirstStageMountVBootV2 : public FirstStageMount {
    ~FirstStageMountVBootV2() override = default;
    ~FirstStageMountVBootV2() override = default;


  protected:
  protected:
    RegenerationAction UeventCallback(const Uevent& uevent) override;
    ListenerAction UeventCallback(const Uevent& uevent) override;
    bool GetRequiredDevices() override;
    bool GetRequiredDevices() override;
    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
    bool InitAvbHandle();
    bool InitAvbHandle();
@@ -141,49 +144,60 @@ bool FirstStageMount::DoFirstStageMount() {
}
}


bool FirstStageMount::InitDevices() {
bool FirstStageMount::InitDevices() {
    if (!GetRequiredDevices()) return false;
    return GetRequiredDevices() && InitRequiredDevices();

    InitRequiredDevices();

    // InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
    // So if it isn't empty here, it means some partitions are not found.
    if (!required_devices_partition_names_.empty()) {
        LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
                   << android::base::Join(required_devices_partition_names_, ", ");
        return false;
    } else {
        return true;
    }
}
}


// Creates devices with uevent->partition_name matching one in the member variable
// Creates devices with uevent->partition_name matching one in the member variable
// required_devices_partition_names_. Found partitions will then be removed from it
// required_devices_partition_names_. Found partitions will then be removed from it
// for the subsequent member function to check which devices are NOT created.
// for the subsequent member function to check which devices are NOT created.
void FirstStageMount::InitRequiredDevices() {
bool FirstStageMount::InitRequiredDevices() {
    if (required_devices_partition_names_.empty()) {
    if (required_devices_partition_names_.empty()) {
        return;
        return true;
    }
    }


    if (need_dm_verity_) {
    if (need_dm_verity_) {
        const std::string dm_path = "/devices/virtual/misc/device-mapper";
        const std::string dm_path = "/devices/virtual/misc/device-mapper";
        uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path,
        bool found = false;
                                                  [this, &dm_path](const Uevent& uevent) {
        auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
            if (uevent.path == dm_path) {
            if (uevent.path == dm_path) {
                device_handler_.HandleDeviceEvent(uevent);
                device_handler_.HandleDeviceEvent(uevent);
                                                          return RegenerationAction::kStop;
                found = true;
                return ListenerAction::kStop;
            }
            return ListenerAction::kContinue;
        };
        uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
        if (!found) {
            uevent_listener_.Poll(dm_callback, 10s);
        }
        if (!found) {
            LOG(ERROR) << "device-mapper device not found";
            return false;
        }
        }
                                                      return RegenerationAction::kContinue;
                                                  });
    }
    }


    uevent_listener_.RegenerateUevents(
    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
        [this](const Uevent& uevent) { return UeventCallback(uevent); });
    uevent_listener_.RegenerateUevents(uevent_callback);

    // UeventCallback() will remove found partitions from required_devices_partition_names_.
    // So if it isn't empty here, it means some partitions are not found.
    if (!required_devices_partition_names_.empty()) {
        uevent_listener_.Poll(uevent_callback, 10s);
    }

    if (!required_devices_partition_names_.empty()) {
        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found: "
                   << android::base::Join(required_devices_partition_names_, ", ");
        return false;
    }

    return true;
}
}


RegenerationAction FirstStageMount::UeventCallback(const Uevent& uevent) {
ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
    // Ignores everything that is not a block device.
    // Ignores everything that is not a block device.
    if (uevent.subsystem != "block") {
    if (uevent.subsystem != "block") {
        return RegenerationAction::kContinue;
        return ListenerAction::kContinue;
    }
    }


    if (!uevent.partition_name.empty()) {
    if (!uevent.partition_name.empty()) {
@@ -192,34 +206,46 @@ RegenerationAction FirstStageMount::UeventCallback(const Uevent& uevent) {
        // suffix when A/B is used.
        // suffix when A/B is used.
        auto iter = required_devices_partition_names_.find(uevent.partition_name);
        auto iter = required_devices_partition_names_.find(uevent.partition_name);
        if (iter != required_devices_partition_names_.end()) {
        if (iter != required_devices_partition_names_.end()) {
            LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
            LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
            required_devices_partition_names_.erase(iter);
            required_devices_partition_names_.erase(iter);
            device_handler_.HandleDeviceEvent(uevent);
            device_handler_.HandleDeviceEvent(uevent);
            if (required_devices_partition_names_.empty()) {
            if (required_devices_partition_names_.empty()) {
                return RegenerationAction::kStop;
                return ListenerAction::kStop;
            } else {
            } else {
                return RegenerationAction::kContinue;
                return ListenerAction::kContinue;
            }
            }
        }
        }
    }
    }
    // Not found a partition or find an unneeded partition, continue to find others.
    // Not found a partition or find an unneeded partition, continue to find others.
    return RegenerationAction::kContinue;
    return ListenerAction::kContinue;
}
}


// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
    const std::string device_name(basename(verity_device.c_str()));
    const std::string device_name(basename(verity_device.c_str()));
    const std::string syspath = "/sys/block/" + device_name;
    const std::string syspath = "/sys/block/" + device_name;
    bool found = false;


    uevent_listener_.RegenerateUeventsForPath(
    auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
        syspath, [&device_name, &verity_device, this](const Uevent& uevent) {
        if (uevent.device_name == device_name) {
        if (uevent.device_name == device_name) {
            LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
            LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
            device_handler_.HandleDeviceEvent(uevent);
            device_handler_.HandleDeviceEvent(uevent);
                return RegenerationAction::kStop;
            found = true;
            return ListenerAction::kStop;
        }
        }
            return RegenerationAction::kContinue;
        return ListenerAction::kContinue;
        });
    };

    uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
    if (!found) {
        uevent_listener_.Poll(verity_callback, 10s);
    }
    if (!found) {
        LOG(ERROR) << "dm-verity device not found";
        return false;
    }

    return true;
}
}


bool FirstStageMount::MountPartitions() {
bool FirstStageMount::MountPartitions() {
@@ -285,7 +311,7 @@ bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
        } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
        } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
            // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
            // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
            // Needs to create it because ueventd isn't started in init first stage.
            // Needs to create it because ueventd isn't started in init first stage.
            InitVerityDevice(fstab_rec->blk_device);
            return InitVerityDevice(fstab_rec->blk_device);
        } else {
        } else {
            return false;
            return false;
        }
        }
@@ -345,7 +371,7 @@ bool FirstStageMountVBootV2::GetRequiredDevices() {
    return true;
    return true;
}
}


RegenerationAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
    // Check if this uevent corresponds to one of the required partitions and store its symlinks if
    // Check if this uevent corresponds to one of the required partitions and store its symlinks if
    // so, in order to create FsManagerAvbHandle later.
    // so, in order to create FsManagerAvbHandle later.
    // Note that the parent callback removes partitions from the list of required partitions
    // Note that the parent callback removes partitions from the list of required partitions
+34 −16
Original line number Original line Diff line number Diff line
@@ -121,8 +121,8 @@ bool UeventListener::ReadUevent(Uevent* uevent) const {
// make sure we don't overrun the socket's buffer.
// make sure we don't overrun the socket's buffer.
//
//


RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d,
ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,
                                                           RegenerateCallback callback) const {
                                                       const ListenerCallback& callback) const {
    int dfd = dirfd(d);
    int dfd = dirfd(d);


    int fd = openat(dfd, "uevent", O_WRONLY);
    int fd = openat(dfd, "uevent", O_WRONLY);
@@ -132,7 +132,7 @@ RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d,


        Uevent uevent;
        Uevent uevent;
        while (ReadUevent(&uevent)) {
        while (ReadUevent(&uevent)) {
            if (callback(uevent) == RegenerationAction::kStop) return RegenerationAction::kStop;
            if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
        }
        }
    }
    }


@@ -147,49 +147,67 @@ RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d,
        if (d2 == 0) {
        if (d2 == 0) {
            close(fd);
            close(fd);
        } else {
        } else {
            if (RegenerateUeventsForDir(d2.get(), callback) == RegenerationAction::kStop) {
            if (RegenerateUeventsForDir(d2.get(), callback) == ListenerAction::kStop) {
                return RegenerationAction::kStop;
                return ListenerAction::kStop;
            }
            }
        }
        }
    }
    }


    // default is always to continue looking for uevents
    // default is always to continue looking for uevents
    return RegenerationAction::kContinue;
    return ListenerAction::kContinue;
}
}


RegenerationAction UeventListener::RegenerateUeventsForPath(const std::string& path,
ListenerAction UeventListener::RegenerateUeventsForPath(const std::string& path,
                                                            RegenerateCallback callback) const {
                                                        const ListenerCallback& callback) const {
    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
    if (!d) return RegenerationAction::kContinue;
    if (!d) return ListenerAction::kContinue;


    return RegenerateUeventsForDir(d.get(), callback);
    return RegenerateUeventsForDir(d.get(), callback);
}
}


static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};


void UeventListener::RegenerateUevents(RegenerateCallback callback) const {
void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
    for (const auto path : kRegenerationPaths) {
    for (const auto path : kRegenerationPaths) {
        if (RegenerateUeventsForPath(path, callback) == RegenerationAction::kStop) return;
        if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;
    }
    }
}
}


void UeventListener::DoPolling(PollCallback callback) const {
void UeventListener::Poll(const ListenerCallback& callback,
                          const std::optional<std::chrono::milliseconds> relative_timeout) const {
    using namespace std::chrono;

    pollfd ufd;
    pollfd ufd;
    ufd.events = POLLIN;
    ufd.events = POLLIN;
    ufd.fd = device_fd_;
    ufd.fd = device_fd_;


    auto start_time = steady_clock::now();

    while (true) {
    while (true) {
        ufd.revents = 0;
        ufd.revents = 0;
        int nr = poll(&ufd, 1, -1);

        if (nr <= 0) {
        int timeout_ms = -1;
        if (relative_timeout) {
            auto now = steady_clock::now();
            auto time_elapsed = duration_cast<milliseconds>(now - start_time);
            if (time_elapsed > *relative_timeout) return;

            auto remaining_timeout = *relative_timeout - time_elapsed;
            timeout_ms = remaining_timeout.count();
        }

        int nr = poll(&ufd, 1, timeout_ms);
        if (nr == 0) return;
        if (nr < 0) {
            PLOG(ERROR) << "poll() of uevent socket failed, continuing";
            continue;
            continue;
        }
        }
        if (ufd.revents & POLLIN) {
        if (ufd.revents & POLLIN) {
            // We're non-blocking, so if we receive a poll event keep processing until there
            // We're non-blocking, so if we receive a poll event keep processing until
            // we have exhausted all uevent messages.
            // we have exhausted all uevent messages.
            Uevent uevent;
            Uevent uevent;
            while (ReadUevent(&uevent)) {
            while (ReadUevent(&uevent)) {
                callback(uevent);
                if (callback(uevent) == ListenerAction::kStop) return;
            }
            }
        }
        }
    }
    }
+10 −8
Original line number Original line Diff line number Diff line
@@ -19,7 +19,9 @@


#include <dirent.h>
#include <dirent.h>


#include <chrono>
#include <functional>
#include <functional>
#include <optional>


#include <android-base/unique_fd.h>
#include <android-base/unique_fd.h>


@@ -27,26 +29,26 @@


#define UEVENT_MSG_LEN 2048
#define UEVENT_MSG_LEN 2048


enum class RegenerationAction {
enum class ListenerAction {
    kStop = 0,  // Stop regenerating uevents as we've handled the one(s) we're interested in.
    kStop = 0,  // Stop regenerating uevents as we've handled the one(s) we're interested in.
    kContinue,  // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
    kContinue,  // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
};
};


using RegenerateCallback = std::function<RegenerationAction(const Uevent&)>;
using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
using PollCallback = std::function<void(const Uevent&)>;


class UeventListener {
class UeventListener {
  public:
  public:
    UeventListener();
    UeventListener();


    void RegenerateUevents(RegenerateCallback callback) const;
    void RegenerateUevents(const ListenerCallback& callback) const;
    RegenerationAction RegenerateUeventsForPath(const std::string& path,
    ListenerAction RegenerateUeventsForPath(const std::string& path,
                                                RegenerateCallback callback) const;
                                            const ListenerCallback& callback) const;
    void DoPolling(PollCallback callback) const;
    void Poll(const ListenerCallback& callback,
              const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;


  private:
  private:
    bool ReadUevent(Uevent* uevent) const;
    bool ReadUevent(Uevent* uevent) const;
    RegenerationAction RegenerateUeventsForDir(DIR* d, RegenerateCallback callback) const;
    ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;


    android::base::unique_fd device_fd_;
    android::base::unique_fd device_fd_;
};
};
+3 −2
Original line number Original line Diff line number Diff line
@@ -138,7 +138,7 @@ void ColdBoot::RegenerateUevents() {
        HandleFirmwareEvent(uevent);
        HandleFirmwareEvent(uevent);


        uevent_queue_.emplace_back(std::move(uevent));
        uevent_queue_.emplace_back(std::move(uevent));
        return RegenerationAction::kContinue;
        return ListenerAction::kContinue;
    });
    });
}
}


@@ -266,9 +266,10 @@ int ueventd_main(int argc, char** argv) {
        cold_boot.Run();
        cold_boot.Run();
    }
    }


    uevent_listener.DoPolling([&device_handler](const Uevent& uevent) {
    uevent_listener.Poll([&device_handler](const Uevent& uevent) {
        HandleFirmwareEvent(uevent);
        HandleFirmwareEvent(uevent);
        device_handler.HandleDeviceEvent(uevent);
        device_handler.HandleDeviceEvent(uevent);
        return ListenerAction::kContinue;
    });
    });


    return 0;
    return 0;