use crate::types::{
util::{read_uevent, write_uevent},
UEvent,
UEventAction,
MODULE_PATH,
SYSFS_PATH,
};
use goblin::elf::{section_header::SHT_PROGBITS, Elf};
use nix::{
kmod::{delete_module, finit_module, init_module, DeleteModuleFlags, ModuleInitFlags},
sys::utsname::uname,
};
use std::{
collections::HashMap,
ffi::CString,
fs,
fs::DirEntry,
io::BufRead,
mem::size_of,
path::{Path, PathBuf},
};
use walkdir::WalkDir;
const SIGNATURE_MAGIC: &[u8] = b"~Module signature appended~\n";
#[derive(Debug, Clone, Copy)]
pub enum Taint {
Proprietary,
OutOfTree,
Forced,
Staging,
Unsigned,
}
#[derive(Debug, Clone, Copy)]
pub enum Type {
BuiltIn,
Dynamic,
}
#[derive(Debug, Clone, Copy)]
pub enum Status {
Live,
Coming,
Going,
}
#[derive(Debug)]
pub struct LoadedModule {
name: String,
module_type: Type,
path: PathBuf,
}
impl LoadedModule {
fn from_dir(path: &Path) -> Self {
let module_type = if path.join("coresize").exists() {
Type::Dynamic
} else {
Type::BuiltIn
};
Self {
name: path.file_stem().unwrap().to_str().unwrap().trim().into(),
module_type,
path: path.into(),
}
}
pub fn from_name(name: &str) -> Self {
Self::from_dir(&Path::new(SYSFS_PATH).join("module").join(name))
}
pub fn from_loaded() -> Vec<Self> {
let dir = Path::new(SYSFS_PATH).join("module");
let mut mods = Vec::new();
for module in fs::read_dir(dir).unwrap() {
let module: DirEntry = module.unwrap();
let m = Self::from_dir(&module.path());
if let Type::BuiltIn = m.module_type() {
continue;
}
mods.push(m);
}
mods
}
pub fn unload(self) {
delete_module(
&CString::new(self.name).unwrap(),
DeleteModuleFlags::O_NONBLOCK,
)
.unwrap();
}
pub unsafe fn force_unload(self) {
delete_module(
&CString::new(self.name).unwrap(),
DeleteModuleFlags::O_NONBLOCK | DeleteModuleFlags::O_TRUNC,
)
.unwrap();
}
pub fn name(&self) -> &str {
self.path.file_stem().unwrap().to_str().unwrap()
}
pub fn module_type(&self) -> Type {
self.module_type
}
pub fn parameters(&self) -> HashMap<String, Vec<u8>> {
todo!()
}
pub fn ref_count(&self) -> Option<u32> {
fs::read_to_string(self.path.join("refcnt"))
.map(|s| s.trim().parse().unwrap())
.ok()
}
pub fn size(&self) -> u64 {
fs::read_to_string(self.path.join("coresize"))
.map(|s| s.trim().parse().unwrap())
.unwrap()
}
pub fn taint(&self) -> Option<Taint> {
match fs::read_to_string(self.path.join("taint")).unwrap().trim() {
"P" => Some(Taint::Proprietary),
"O" => Some(Taint::OutOfTree),
"F" => Some(Taint::Forced),
"C" => Some(Taint::Staging),
"E" => Some(Taint::Unsigned),
_ => None,
}
}
pub fn holders(&self) -> Vec<Self> {
let mut v = Vec::new();
for re in fs::read_dir(self.path.join("holders")).unwrap() {
let re: DirEntry = re.unwrap();
v.push(Self::from_dir(&re.path()))
}
v
}
pub fn file_path(&self) -> PathBuf {
todo!()
}
pub fn status(&self) -> Status {
match fs::read_to_string(self.path.join("initstate"))
.unwrap()
.trim()
{
"live" => Status::Live,
"coming" => Status::Coming,
"going" => Status::Going,
_ => panic!("Unknown module state"),
}
}
}
impl UEvent for LoadedModule {
fn write(&self, action: UEventAction, uuid: Option<String>, args: HashMap<String, String>) {
write_uevent(&self.path.join("uevent"), action, uuid, args)
}
fn read(&self) -> HashMap<String, String> {
read_uevent(&self.path.join("uevent"))
}
}
#[derive(Debug)]
pub struct ModuleFile {
name: String,
path: PathBuf,
}
impl ModuleFile {
pub fn from_name(name: &str) -> Self {
let path = Path::new(MODULE_PATH)
.join(uname().release())
.join("kernel");
for entry in WalkDir::new(path) {
let entry = entry.unwrap();
if !entry.file_type().is_file() {
continue;
}
let m = if entry.path().extension().unwrap() == "ko" {
entry.path().file_stem().unwrap()
} else {
Path::new(entry.path().file_stem().unwrap())
.file_stem()
.unwrap()
};
if m == name {
return Self {
name: name.into(),
path: entry.into_path(),
};
}
}
panic!("Couldn't find module {}", name);
}
pub fn from_path(path: &Path) -> Self {
assert!(path.exists());
Self {
name: path.file_stem().unwrap().to_str().unwrap().into(),
path: path.into(),
}
}
pub fn load(&self, param: &str) -> LoadedModule {
let img = fs::read(&self.path).unwrap();
init_module(&img, &CString::new(param).unwrap()).unwrap();
LoadedModule::from_dir(&Path::new(SYSFS_PATH).join("module").join(&self.name))
}
pub unsafe fn force_load(&self, param: &str) -> LoadedModule {
let file = fs::File::open(&self.path).unwrap();
finit_module(
&file,
&CString::new(param).unwrap(),
ModuleInitFlags::MODULE_INIT_IGNORE_MODVERSIONS
| ModuleInitFlags::MODULE_INIT_IGNORE_VERMAGIC,
)
.unwrap();
LoadedModule::from_dir(&Path::new(SYSFS_PATH).join("module").join(&self.name))
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn name(&self) -> &str {
&self.name
}
pub fn info(&self) -> ModInfo {
let f = fs::read(&self.path).unwrap();
let elf = Elf::parse(&f).unwrap();
for header in elf.section_headers {
if header.sh_type != SHT_PROGBITS {
continue;
}
let name = elf.shdr_strtab.get(header.sh_name).unwrap().unwrap();
if name == ".modinfo" {
let mut map = HashMap::new();
for kv in BufRead::split(&f[header.file_range()], b'\0') {
let kv: Vec<u8> = kv.unwrap();
let s = String::from_utf8(kv).unwrap();
let mut s = s.splitn(2, '=');
let key = s.next().unwrap().to_string();
let value = s.next().unwrap().to_string();
let vec = map.entry(key).or_insert(Vec::new());
if !value.is_empty() {
vec.push(value);
}
}
fn y_n(s: &str) -> bool {
if s == "Y" {
true
} else {
false
}
}
fn one(map: &mut HashMap<String, Vec<String>>, key: &str) -> String {
map.remove(key).map(|mut v| v.remove(0)).unwrap_or_default()
}
fn more(map: &mut HashMap<String, Vec<String>>, key: &str) -> Vec<String> {
map.remove(key).unwrap_or_default()
}
let mut parameters = Vec::new();
for ((name, description), type_) in map
.remove("parm")
.unwrap()
.into_iter()
.map(|s| {
let mut i = s.splitn(2, ':');
let name = i.next().unwrap();
let desc = i.next().unwrap();
(name.to_string(), desc.to_string())
})
.zip(map.remove("parmtype").unwrap().into_iter().map(|s| {
let mut i = s.splitn(2, ':');
i.next().unwrap();
let typ = i.next().unwrap();
typ.to_string()
}))
{
parameters.push(ModParam {
name,
description,
type_,
})
}
return ModInfo {
alias: more(&mut map, "alias"),
soft_dependencies: more(&mut map, "softdep"),
license: one(&mut map, "license"),
authors: more(&mut map, "author"),
description: one(&mut map, "description"),
version: one(&mut map, "version"),
firmware: more(&mut map, "firmware"),
version_magic: one(&mut map, "vermagic"),
name: one(&mut map, "name"),
in_tree: y_n(&one(&mut map, "intree")),
retpoline: y_n(&one(&mut map, "retpoline")),
staging: y_n(&one(&mut map, "staging")),
dependencies: more(&mut map, "depends"),
source_checksum: one(&mut map, "srcversion"),
parameters,
};
}
}
panic!("Missing .modinfo")
}
pub fn has_signature(&self) -> bool {
let f = fs::read(&self.path).unwrap();
f.ends_with(SIGNATURE_MAGIC)
}
fn _signature(&self) -> Option<ModSig> {
let f = fs::read(&self.path).unwrap();
if f.ends_with(SIGNATURE_MAGIC) {
let len = f.len() - size_of::<RawModSig>() - SIGNATURE_MAGIC.len();
let sig: &[u8] = &f[len..];
let mut sig = unsafe { (sig.as_ptr() as *const RawModSig).read_unaligned() };
sig.signature_length = u32::from_be(sig.signature_length);
dbg!(sig);
let data_start = len - sig.signature_length as usize;
let _sig_data: &[u8] = &f[data_start..][..sig.signature_length as usize];
todo!()
} else {
None
}
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
struct RawModSig {
algorithm: u8,
hash: u8,
id_type: u8,
signer_length: u8,
key_id_length: u8,
_pad: [u8; 3],
signature_length: u32,
}
#[derive(Debug)]
struct ModSig {
signer: String,
}
#[derive(Debug)]
pub struct ModParam {
pub name: String,
pub description: String,
pub type_: String,
}
#[derive(Debug)]
pub struct ModInfo {
pub alias: Vec<String>,
pub soft_dependencies: Vec<String>,
pub license: String,
pub authors: Vec<String>,
pub description: String,
pub version: String,
pub firmware: Vec<String>,
pub version_magic: String,
pub name: String,
pub in_tree: bool,
pub retpoline: bool,
pub staging: bool,
pub dependencies: Vec<String>,
pub source_checksum: String,
pub parameters: Vec<ModParam>,
}