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: 8320778425 ("toolbox/modprobe: Fallback to /lib/modules/<uname> ")
Bug: 282917063
Bug: 254835242
Merged-In: I5368f5cff139ba3165323a6a91066be38bfa0736
Change-Id: I5368f5cff139ba3165323a6a91066be38bfa0736
This commit is contained in:
Will McVicker 2023-05-25 16:43:31 -07:00
parent 7003fba5f2
commit e067e96da2

View file

@ -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();