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

Commit e067e96d authored by Will McVicker's avatar Will McVicker
Browse files

toolbox/modprobe: Fix fallback path when mod_dirs is empty

Due to GKI, the kernel UTS release string will not always (if ever)
match the vendor's UTS release string that is used to create the
initramfs file structure -- /lib/modules/<vendor uname>. This causes
module load failures when `-d DIR` is omitted. To fix this, we can
include all of the versions under /lib/modules that match the kernel's
major and minor version instead of directly using the value of uname().
In addition, we can also support modules being loaded directly from
/lib/modules.

Test: verify GKI kernel + initramfs with different UTS strings
Test: verify GKI kernel + initramfs with modules directly in /lib/modules
Fixes: 83207784 ("toolbox/modprobe: Fallback to /lib/modules/<uname> ")
Bug: 282917063
Bug: 254835242
Merged-In: I5368f5cff139ba3165323a6a91066be38bfa0736
Change-Id: I5368f5cff139ba3165323a6a91066be38bfa0736
parent 7003fba5
Loading
Loading
Loading
Loading
+36 −8
Original line number Diff line number Diff line
@@ -85,6 +85,26 @@ void MyLogger(android::base::LogId id, android::base::LogSeverity severity, cons
    }
}

// Find directories in format of "/lib/modules/x.y.z-*".
static int KernelVersionNameFilter(const dirent* de) {
    unsigned int major, minor;
    static std::string kernel_version;
    utsname uts;

    if (kernel_version.empty()) {
        if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
            LOG(ERROR) << "Could not parse the kernel version from uname";
            return 0;
        }
        kernel_version = android::base::StringPrintf("%u.%u", major, minor);
    }

    if (android::base::StartsWith(de->d_name, kernel_version)) {
        return 1;
    }
    return 0;
}

}  // anonymous namespace

extern "C" int modprobe_main(int argc, char** argv) {
@@ -192,9 +212,22 @@ extern "C" int modprobe_main(int argc, char** argv) {
    }

    if (mod_dirs.empty()) {
        utsname uts;
        uname(&uts);
        mod_dirs.emplace_back(android::base::StringPrintf("/lib/modules/%s", uts.release));
        static constexpr auto LIB_MODULES_PREFIX = "/lib/modules/";
        dirent** kernel_dirs = NULL;

        int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);
        if (n == -1) {
            PLOG(ERROR) << "Failed to scan dir " << LIB_MODULES_PREFIX;
            return EXIT_FAILURE;
        } else if (n > 0) {
            while (n--) {
                mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));
            }
        }
        free(kernel_dirs);

        // Allow modules to be directly inside /lib/modules
        mod_dirs.emplace_back(LIB_MODULES_PREFIX);
    }

    LOG(DEBUG) << "mode is " << mode;
@@ -212,11 +245,6 @@ extern "C" int modprobe_main(int argc, char** argv) {
            return EXIT_FAILURE;
        }
    }
    if (mod_dirs.empty()) {
        LOG(ERROR) << "No module configuration directories given.";
        print_usage();
        return EXIT_FAILURE;
    }
    if (parameter_count && modules.size() > 1) {
        LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
        print_usage();