~danyspin97/initrz

78367ad283c51c724dfadab12411c857a6ce584d — Danilo Spinella 4 months ago 4a79f3a
Refactor common code into a new module and improve module packing
M Cargo.lock => Cargo.lock +12 -0
@@ 238,6 238,15 @@ dependencies = [
]

[[package]]
name = "common"
version = "0.1.0"
dependencies = [
 "anyhow",
 "log",
 "rayon",
]

[[package]]
name = "crossbeam-channel"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 402,6 411,7 @@ version = "0.1.0"
dependencies = [
 "anyhow",
 "bstr",
 "common",
 "dowser",
 "either",
 "glob",


@@ 417,6 427,7 @@ dependencies = [
 "serde",
 "serde_yaml",
 "simplelog",
 "xz2",
]

[[package]]


@@ 558,6 569,7 @@ version = "0.1.0"
dependencies = [
 "anyhow",
 "clap 3.0.0-beta.2",
 "common",
 "dowser",
 "libc",
 "log",

M Cargo.toml => Cargo.toml +1 -0
@@ 1,6 1,7 @@
[workspace]

members = [
    "src/common",
    "src/initrz",
    "src/mkinitrz",
]

A src/common/Cargo.toml => src/common/Cargo.toml +12 -0
@@ 0,0 1,12 @@
[package]
name = "common"
version = "0.1.0"
authors = ["Danilo Spinella <oss@danyspin97.org>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
log = "0.4"
rayon = "1.5"


A src/common/src/lib.rs => src/common/src/lib.rs +3 -0
@@ 0,0 1,3 @@
mod modules;

pub use modules::Modules;

A src/common/src/modules.rs => src/common/src/modules.rs +134 -0
@@ 0,0 1,134 @@
use std::{
    collections::HashMap,
    fs::File,
    io::{BufRead, BufReader},
    path::Path,
};

use anyhow::{bail, Context, Result};
use log::warn;

pub struct Module {
    pub filename: String,
    pub deps: Vec<String>,
}

pub struct Modules {
    data: HashMap<String, Module>,
}

impl Modules {
    pub fn new(kernel_root: &Path) -> Result<Modules> {
        Ok(Modules {
            data: parse_module_dep(&kernel_root.join("modules.dep"))?,
        })
    }

    pub fn get(&self, module: &str) -> Option<&Module> {
        self.data.get(module)
    }
}

pub fn parse_module_dep(filename: &Path) -> Result<HashMap<String, Module>> {
    let file =
        File::open(filename).with_context(|| format!("unable to open filename {:?}", filename))?;
    let lines = BufReader::new(file).lines();
    Ok(lines
        .filter_map(|line: Result<String, _>| line.ok())
        .map(|line| -> Result<(String, Module)> {
            let token_index = line
                .find(':')
                .with_context(|| format!("could not find ':' in line:\n{}", line))?;
            let module_filename = &line[0..token_index];
            let module = if let Ok(module) = get_module_name(module_filename) {
                module
            } else {
                bail!("{} is not a valid module name", module_filename);
            };
            let mut deps: Vec<String> = Vec::new();
            let rest_of_line = &line[token_index + 1..];
            if rest_of_line.len() != 0 {
                // iter.rest() returns " kernel/..." so skip the first space
                let split = rest_of_line[1..].split(' ');
                split
                    .filter_map(|dep| get_module_name(dep).ok())
                    .for_each(|dep| deps.push(dep));

                deps.reverse();
            }
            Ok((
                module,
                Module {
                    filename: module_filename.to_string(),
                    deps,
                },
            ))
        })
        .filter_map(|res| res.ok())
        .collect())
}

fn get_module_name(filename: &str) -> Result<String> {
    Ok(Path::new(filename)
        .file_stem()
        .and_then(|module| std::path::Path::new(module).file_stem())
        .with_context(|| format!("failed to get module name of file {}", filename))?
        .to_str()
        .with_context(|| {
            format!(
                "failed to convert the module name in file {} from OsStr to Str",
                filename
            )
        })?
        .to_string())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_module_dep_test() {
        let filename = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/test/module.dep"));
        let map = parse_module_dep(filename).unwrap();

        let mut expected_map = HashMap::new();

        let mut mhi_deps: Vec<String> = Vec::new();
        mhi_deps.push("mhi".to_string());
        mhi_deps.push("ns".to_string());
        mhi_deps.push("qrtr".to_string());
        expected_map.insert(
            "qrtr-mhi".to_string(),
            Module {
                filename: String::from("kernel/net/qrtr/qrtr-mhi.ko.xz"),
                deps: mhi_deps,
            },
        );

        let mut nvidia_uvm_deps: Vec<String> = Vec::new();
        nvidia_uvm_deps.push("nvidia".to_string());
        expected_map.insert(
            "nvidia-uvm".to_string(),
            Module {
                filename: String::from("kernel/drivers/video/nvidia-uvm.ko.xz"),
                deps: nvidia_uvm_deps,
            },
        );

        expected_map.insert(
            "nvidia".to_string(),
            Module {
                filename: String::from("kernel/drivers/video/nvidia.ko.xz"),
                deps: Vec::new(),
            },
        );

        assert_eq!(map.len(), 3);
        for (module_name, module) in map {
            let expected_module = expected_map.get(&module_name).expect("no module found");
            assert_eq!(module.filename, expected_module.filename);
            assert_eq!(module.deps, expected_module.deps);
        }
    }
}

M src/initrz/Cargo.toml => src/initrz/Cargo.toml +1 -0
@@ 6,6 6,7 @@ edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
common = { path = "../common" }
anyhow = "1.0"
bstr = "0.2"
dowser = "0.2"

M src/initrz/src/module_loader.rs => src/initrz/src/module_loader.rs +4 -124
@@ 15,10 15,7 @@ use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::sync::RwLock;

pub struct Module {
    filename: String,
    deps: Vec<String>,
}
use common::Modules;

pub struct ModAlias {
    pattern: Pattern,


@@ 26,79 23,12 @@ pub struct ModAlias {
}

pub struct ModuleLoader {
    modules: HashMap<String, Module>,
    modules: Modules,
    aliases: Vec<ModAlias>,
    modules_loaded: RwLock<HashSet<String>>,
    kernel_root: PathBuf,
}

pub fn parse_module_dep(filename: &Path) -> Result<HashMap<String, Module>> {
    let file =
        File::open(filename).with_context(|| format!("unable to open filename {:?}", filename))?;
    let lines = BufReader::new(file).lines();
    Ok(lines
        .filter_map(|line: Result<String, _>| line.ok())
        .map(|line| -> Result<(String, Module)> {
            let token_index = line
                .find(':')
                .with_context(|| format!("could not find ':' in line:\n{}", line))?;
            let module_filename = &line[0..token_index];
            let module = get_module_name(module_filename);
            if module.is_err() {
                bail!("{} is not a valid module name", module_filename);
            }
            let module = module.unwrap();
            let mut deps: Vec<String> = Vec::new();
            let rest_of_line = &line[token_index + 1..];
            if rest_of_line.len() != 0 {
                // iter.rest() returns " kernel/..." so skip the first space
                let split = rest_of_line[1..].split(' ');
                for dep in split {
                    let module_dep = get_module_name(dep);
                    if module_dep.is_ok() {
                        deps.push(module_dep.unwrap());
                    } else {
                        warn!("{} is not a valid module name", dep);
                    }
                }

                deps.reverse();
            }
            Ok((
                module,
                Module {
                    filename: module_filename.to_string(),
                    deps,
                },
            ))
        })
        .filter_map(|res| res.ok())
        .collect())

    // modules.insert(
    //     module,
    //     Module {
    //         filename: String::from(module_filename),
    //         deps,
    //     },
    // );
}

fn get_module_name(filename: &str) -> Result<String> {
    Ok(Path::new(filename)
        .file_stem()
        .and_then(|module| std::path::Path::new(module).file_stem())
        .with_context(|| format!("failed to get module name of file {}", filename))?
        .to_str()
        .with_context(|| {
            format!(
                "failed to convert the module name in file {} from OsStr to Str",
                filename
            )
        })?
        .to_string())
}

pub fn parse_module_alias(filename: &Path) -> Result<Vec<ModAlias>> {
    let file =
        File::open(filename).with_context(|| format!("unable to open file {:?}", filename))?;


@@ 130,9 60,9 @@ impl ModuleLoader {
        let kernel_root = Path::new("/lib/modules").join(kernel_version);
        let mut modules = HashSet::new();

        modules.reserve(glob(&kernel_root.join("*.ko").as_os_str().to_string_lossy())?.count());
        modules.reserve(glob(&kernel_root.join("*.ko.xz").as_os_str().to_string_lossy())?.count());
        Ok(ModuleLoader {
            modules: parse_module_dep(&kernel_root.join("modules.dep"))?,
            modules: Modules::new(&kernel_root)?,
            aliases: parse_module_alias(&kernel_root.join("modules.alias"))?,
            modules_loaded: RwLock::new(modules),
            kernel_root,


@@ 183,53 113,3 @@ impl ModuleLoader {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_module_dep_test() {
        let filename = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/test/module.dep"));
        let map = parse_module_dep(filename).unwrap();

        let mut expected_map = HashMap::new();

        let mut mhi_deps: Vec<String> = Vec::new();
        mhi_deps.push("mhi".to_string());
        mhi_deps.push("ns".to_string());
        mhi_deps.push("qrtr".to_string());
        expected_map.insert(
            "qrtr-mhi".to_string(),
            Module {
                filename: String::from("kernel/net/qrtr/qrtr-mhi.ko.xz"),
                deps: mhi_deps,
            },
        );

        let mut nvidia_uvm_deps: Vec<String> = Vec::new();
        nvidia_uvm_deps.push("nvidia".to_string());
        expected_map.insert(
            "nvidia-uvm".to_string(),
            Module {
                filename: String::from("kernel/drivers/video/nvidia-uvm.ko.xz"),
                deps: nvidia_uvm_deps,
            },
        );

        expected_map.insert(
            "nvidia".to_string(),
            Module {
                filename: String::from("kernel/drivers/video/nvidia.ko.xz"),
                deps: Vec::new(),
            },
        );

        assert_eq!(map.len(), 3);
        for (module_name, module) in map {
            let expected_module = expected_map.get(&module_name).expect("no module found");
            assert_eq!(module.filename, expected_module.filename);
            assert_eq!(module.deps, expected_module.deps);
        }
    }
}

M src/mkinitrz/Cargo.toml => src/mkinitrz/Cargo.toml +1 -0
@@ 7,6 7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
common = { path = "../common" }
anyhow = "1.0"
clap = { git = "https://github.com/clap-rs/clap/" }
dowser = "0.2"

M src/mkinitrz/src/initramfs.rs => src/mkinitrz/src/initramfs.rs +66 -64
@@ 6,13 6,13 @@ use std::{
    path::{Path, PathBuf},
};

use anyhow::Result;
use anyhow::{bail, Context, Result};
use log::debug;

use crate::config::Config;
use crate::depend;
use crate::module::Module;
use crate::modules;
use crate::initramfs_modules;
use crate::initramfs_type::InitramfsType;
use crate::newc::{self, Archive, Entry, EntryBuilder};

const ROOT_DIRECTORIES: [&str; 9] = [


@@ 46,30 46,67 @@ pub struct Initramfs {
}

impl Initramfs {
    pub fn new(kver: &str, config: Config) -> Result<Initramfs> {
        let mut initramfs = Initramfs::new_common_structure(kver, &config)?;

        let modules = modules::get_general_modules(kver, config.modules)?;
        initramfs.add_modules(kver, modules)?;

    pub fn new(initramfs_type: InitramfsType, kroot: PathBuf, config: Config) -> Result<Initramfs> {
        let mut initramfs = Initramfs::new_basic_structure()?;
        initramfs.init(initramfs_type, kroot, config)?;
        Ok(initramfs)
    }

    pub fn with_host_settings(kver: &str, config: Config) -> Result<Initramfs> {
        let mut initramfs = Initramfs::new_common_structure(kver, &config)?;

        let crypttab = Path::new("/etc/crypttab.initramfs");
        if crypttab.exists() {
            initramfs.add_file(crypttab)?;
    fn init(
        &mut self,
        initramfs_type: InitramfsType,
        kroot: PathBuf,
        config: Config,
    ) -> Result<()> {
        let mut initrz: PathBuf =
            Path::new(&env::var("INITRZ").unwrap_or("target/release/initrz".to_string())).into();
        if !initrz.exists() {
            initrz = Path::new("/sbin/initrz").into();
            if !initrz.exists() {
                bail!("unable to find initrz executable. Please set INITRZ environment variable");
            }
        }
        self.add_elf_with_path(&initrz, Path::new("/init"))?;

        let modules = modules::get_host_modules(kver, config.modules)?;
        initramfs.add_modules(kver, modules)?;
        self.add_elf(Path::new("/sbin/vgchange"))?;
        self.add_elf(Path::new("/sbin/vgmknodes"))?;

        Ok(initramfs)
        self.add_elf(Path::new("/bin/busybox"))?;

        let ld_conf = Path::new("/etc/ld.so.conf");
        self.add_entry(
            ld_conf,
            EntryBuilder::file(ld_conf, Vec::new())
                .with_metadata(&fs::metadata(&ld_conf)?)
                .build(),
        );

        self.add_file(&kroot.join("modules.dep"))?;
        self.add_file(&kroot.join("modules.alias"))?;

        self.apply_config(&config);

        initramfs_modules::get_modules(&initramfs_type, &kroot, config.modules)?
            .iter()
            .try_for_each(|module| -> Result<()> {
                self.add_file(&module)?;
                Ok(())
            })?;

        match initramfs_type {
            InitramfsType::Host => {
                let crypttab = Path::new("/etc/crypttab.initramfs");
                if crypttab.exists() {
                    self.add_file(crypttab)?;
                }
            }
            InitramfsType::General => {}
        }

        Ok(())
    }

    fn new_common_structure(kver: &str, config: &Config) -> Result<Initramfs> {
    fn new_basic_structure() -> Result<Initramfs> {
        let mut entries = Vec::new();
        let mut files: HashSet<PathBuf> = HashSet::new();



@@ 87,35 124,7 @@ impl Initramfs {
            )
        });

        let mut initramfs = Initramfs { entries, files };

        let mut initrz: PathBuf =
            Path::new(&env::var("INITRZ").unwrap_or("target/release/initrz".to_string())).into();
        if !initrz.exists() {
            initrz = Path::new("/sbin/initrz").into();
        }
        initramfs.add_elf_with_path(&initrz, Path::new("/init"))?;

        initramfs.add_elf(Path::new("/sbin/vgchange"))?;
        initramfs.add_elf(Path::new("/sbin/vgmknodes"))?;

        initramfs.add_elf(Path::new("/bin/busybox"))?;

        let ld_conf = Path::new("/etc/ld.so.conf");
        initramfs.add_entry(
            ld_conf,
            EntryBuilder::file(ld_conf, Vec::new())
                .with_metadata(&fs::metadata(&ld_conf)?)
                .build(),
        );

        let kernel_root = Path::new("/lib/modules").join(kver);
        initramfs.add_file(&kernel_root.join("modules.dep"))?;
        initramfs.add_file(&kernel_root.join("modules.alias"))?;

        initramfs.apply_config(config);

        Ok(initramfs)
        Ok(Initramfs { entries, files })
    }

    fn apply_config(&mut self, config: &Config) {}


@@ 164,9 173,15 @@ impl Initramfs {
        );
        self.add_entry(
            &path,
            EntryBuilder::file(&path, fs::read(&file)?)
                .with_metadata(&fs::metadata(file)?)
                .build(),
            EntryBuilder::file(
                &path,
                fs::read(&file).with_context(|| format!("unable to read from file {:?}", file))?,
            )
            .with_metadata(
                &fs::metadata(&file)
                    .with_context(|| format!("unable to read metadata of file {:?}", file))?,
            )
            .build(),
        );
        Ok(true)
    }


@@ 191,19 206,6 @@ impl Initramfs {
        self.entries.push(entry);
    }

    fn add_modules(&mut self, kver: &str, modules: Vec<Module>) -> Result<()> {
        Ok(modules.iter().try_for_each(|module| -> Result<()> {
            let path = &module.path.with_extension("");
            self.add_directory(path.parent().unwrap());
            Ok(self.add_entry(
                &path,
                EntryBuilder::file(&path, module.into_bytes()?)
                    .with_metadata(&fs::metadata(&module.path)?)
                    .build(),
            ))
        })?)
    }

    pub fn into_bytes(self) -> Result<Vec<u8>> {
        Archive::new(self.entries).into_bytes()
    }

A src/mkinitrz/src/initramfs_modules.rs => src/mkinitrz/src/initramfs_modules.rs +171 -0
@@ 0,0 1,171 @@
use std::{
    collections::HashSet,
    convert::TryFrom,
    fs::File,
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
};

use anyhow::{Context, Result};
use dowser::Dowser;
use log::warn;
use rayon::prelude::*;

use crate::initramfs_type::InitramfsType;
use common::Modules;

fn is_general_module(path: &Path, filename: &str) -> bool {
    let path_str = path.as_os_str().to_str().unwrap();
    warn!("{}", path_str);
    // https://github.com/distr1/distri/blob/master/cmd/distri/initrd.go#L45
    if path.starts_with("fs") && !path.starts_with("fs/nls") {
        return true; // file systems
    }
    if path.starts_with("crypto")
        || filename == "dm-crypt.ko.xz"
        || filename == "dm-integrity.ko.xz"
    {
        return true; // disk encryption
    }
    if path.starts_with("drivers/md/") || path.starts_with("lib/") {
        return true; // device mapper
    }
    if path_str.contains("sd_mod")
        || path_str.contains("sr_mod")
        || path_str.contains("usb_storage")
        || path_str.contains("firewire-sbp2")
        || path_str.contains("block")
        || path_str.contains("scsi")
        || path_str.contains("fusion")
        || path_str.contains("nvme")
        || path_str.contains("mmc")
        || path_str.contains("tifm_")
        || path_str.contains("virtio")
        || path_str.contains("drivers/ata/")
        || path_str.contains("drivers/usb/host/")
        || path_str.contains("drivers/usb/storage/")
        || path_str.contains("drivers/firewire/")
    {
        return true; // block devices
    }
    if path.starts_with("drivers/hid/")
        || path.starts_with("drivers/input/keyboard/")
        || path.starts_with("drivers/input/serio/")
        || path.starts_with("usbhid")
    {
        return true; // keyboard input
    }

    false
}

fn insert_module<'a>(
    module_name: &'a str,
    modules: &'a Modules,
    modules_added: &mut HashSet<&'a str>,
) {
    let module = modules.get(module_name).unwrap();
    modules_added.insert(module_name);
    module
        .deps
        .iter()
        .for_each(|dep| insert_module(dep, modules, modules_added))
}

pub fn get_modules(
    initramfs_type: &InitramfsType,
    kroot: &Path,
    additional_modules: Vec<String>,
) -> Result<Vec<PathBuf>> {
    let modules = Modules::new(&kroot)?;
    let mut modules_to_add = match initramfs_type {
        InitramfsType::General => {
            let mut modules_added: HashSet<&str> = HashSet::new();
            let general_modules = get_general_modules(kroot)?;
            // Add dependencies for each module
            general_modules
                .iter()
                .for_each(|module| insert_module(module, &modules, &mut modules_added));
            general_modules
        }
        // We assume dependencies are already loaded when considering host modules
        InitramfsType::Host => get_host_modules()?,
    };
    // Do not mind about replicated modules, Initramfs will handle those
    modules_to_add.extend(additional_modules);
    Ok(modules_to_add
        .par_iter()
        .filter_map(|module_name| {
            if let Some(module) = modules.get(module_name) {
                Some(kroot.join(&module.filename))
            } else {
                None
            }
        })
        .collect::<Vec<PathBuf>>())
}

fn get_general_modules(kroot: &Path) -> Result<Vec<String>> {
    let modules_root = kroot.join("kernel/");
    Ok(Vec::<PathBuf>::try_from(
        Dowser::filtered(move |p: &Path| {
            if !is_module(p) {
                return false;
            }
            let path = p.strip_prefix(&modules_root);
            if path.is_err() {
                return false;
            }
            let path = path.unwrap();
            let filename = path.file_name().unwrap().to_str().unwrap();
            is_general_module(path, filename)
        })
        .with_path(kroot.join("kernel")),
    )?
    .iter()
    .map(|path| get_module_name(path))
    .collect::<Result<Vec<_>>>()?)
}

fn get_module_name(filename: &PathBuf) -> Result<String> {
    Ok(filename
        .file_stem()
        .and_then(|module| std::path::Path::new(module).file_stem())
        .with_context(|| format!("failed to get module name of file {:?}", filename))?
        .to_str()
        .with_context(|| {
            format!(
                "failed to convert the module name in file {:?} from OsStr to Str",
                filename
            )
        })?
        .to_string())
}

fn is_module(p: &Path) -> bool {
    p.extension()
        .filter(|ext| ext.to_str().unwrap_or("") == "xz")
        .is_some()
        && p.file_stem()
            .filter(|stem| {
                Path::new(stem)
                    .extension()
                    .filter(|ext| ext.to_str().unwrap_or("") == "ko")
                    .is_some()
            })
            .is_some()
}

fn get_host_modules() -> Result<Vec<String>> {
    Ok(BufReader::new(
        File::open("/proc/modules").with_context(|| "unable to open file /proc/modules")?,
    )
    .lines()
    .filter_map(|line| line.ok())
    .filter_map(|line| {
        line.split_whitespace()
            .next()
            .and_then(|module| Some(module.to_string()))
    })
    .collect())
}

A src/mkinitrz/src/initramfs_type.rs => src/mkinitrz/src/initramfs_type.rs +4 -0
@@ 0,0 1,4 @@
pub enum InitramfsType {
    Host,
    General,
}

M src/mkinitrz/src/main.rs => src/mkinitrz/src/main.rs +28 -14
@@ 1,13 1,18 @@
mod config;
mod depend;
mod initramfs;
mod module;
mod modules;
mod initramfs_modules;
mod initramfs_type;
mod newc;

use std::{ffi::OsString, fs::File, io::Write, path::Path};
use std::{
    ffi::OsString,
    fs::{self, File},
    io::Write,
    path::Path,
};

use anyhow::Result;
use anyhow::{Context, Result};
use clap::Clap;
use log;
use simplelog::{ColorChoice, LevelFilter, TermLogger, TerminalMode};


@@ 15,6 20,7 @@ use zstd::stream::write::Encoder;

use config::Config;
use initramfs::Initramfs;
use initramfs_type::InitramfsType;

#[derive(Clap)]
#[clap(version = "0.1", author = "danyspin97")]


@@ 56,17 62,25 @@ fn main() -> Result<()> {
        ColorChoice::Auto,
    )?;

    let config = Config::new(&Path::new(&opts.config))?;

    let initramfs = if opts.host {
        Initramfs::with_host_settings(&opts.kver, config)?
    } else {
        Initramfs::new(&opts.kver, config)?
    };
    let initramfs_file = File::create("initramfs.img")?;
    let mut zstd_encoder = Encoder::new(initramfs_file, 3)?;
    let mut zstd_encoder = Encoder::new(
        File::create(&opts.output)
            .with_context(|| format!("unable to create file {:?}", opts.output))?,
        3,
    )?;
    // zstd_encoder.multithread(1)?;
    zstd_encoder.write_all(&initramfs.into_bytes()?)?;
    zstd_encoder.write_all(
        &Initramfs::new(
            if opts.host {
                InitramfsType::Host
            } else {
                InitramfsType::General
            },
            // Canonicalize path to avoid problems with dowser and filter
            fs::canonicalize(Path::new("/lib/modules").join(&opts.kver))?,
            Config::new(&Path::new(&opts.config))?,
        )?
        .into_bytes()?,
    )?;
    zstd_encoder.finish()?;

    Ok(())

D src/mkinitrz/src/module.rs => src/mkinitrz/src/module.rs +0 -29
@@ 1,29 0,0 @@
use std::{
    fs::File,
    io::{BufReader, Read},
    path::{Path, PathBuf},
};

use anyhow::Result;
use xz2::bufread::XzDecoder;

#[derive(Debug)]
pub struct Module {
    pub name: String,
    pub path: PathBuf,
}

impl Module {
    pub fn new(name: String, path: &Path) -> Module {
        Module {
            name,
            path: path.into(),
        }
    }

    pub fn into_bytes(&self) -> Result<Vec<u8>> {
        let mut data = Vec::new();
        XzDecoder::new(BufReader::new(File::open(&self.path)?)).read_to_end(&mut data)?;
        Ok(data)
    }
}

D src/mkinitrz/src/modules.rs => src/mkinitrz/src/modules.rs +0 -120
@@ 1,120 0,0 @@
use std::{
    collections::HashMap,
    convert::TryFrom,
    path::{Path, PathBuf},
};

use anyhow::{Context, Result};
use dowser::Dowser;
use log::warn;
use rayon::prelude::*;

use crate::module::Module;

pub type Modules = Vec<Module>;

pub fn get_general_modules(kver: &str, additional_modules: Vec<String>) -> Result<Modules> {
    let modules: Vec<String> = get_modules_path(kver)?
        .iter_mut()
        .map(|(key, _)| key.clone())
        .collect();

    // let additional_modules: Vec<&str> = additional_modules
    //     .iter()
    //     .filter(|module| modules.contains(&module.as_str()).clone())
    //     .map(|s| s.as_str())
    //     .collect();
    // modules.extend(additional_modules);

    common_modules_gen(
        kver,
        &modules
            .iter()
            .map(|s| s.as_str())
            .collect::<Vec<&str>>()
            .as_slice(),
    )
}

pub fn get_host_modules(kver: &str, additional_modules: Vec<String>) -> Result<Modules> {
    let mut modules = Vec::new();
    let additional_modules: Vec<&str> = additional_modules
        .iter()
        .filter(|module| modules.contains(&module.as_str()).clone())
        .map(|s| s.as_str())
        .collect();
    modules.extend(additional_modules);

    common_modules_gen(kver, &modules)
}

fn common_modules_gen(kver: &str, modules: &[&str]) -> Result<Modules> {
    let modules_path = get_modules_path(kver)?;

    Ok(modules
        .par_iter()
        .map(|name| -> Result<Module> {
            let module_name = name.to_string();
            let path = modules_path
                .get(&module_name)
                .with_context(|| format!("unable to find module {}", name))?
                .clone();
            Ok(Module {
                name: module_name,
                path,
            })
        })
        .filter_map(|module| -> Option<Module> {
            if module.is_err() {
                warn!("{:?}", module);
            }
            module.ok()
        })
        .collect())
}

fn get_modules_path(kver: &str) -> Result<HashMap<String, PathBuf>> {
    Ok(Vec::<PathBuf>::try_from(
        Dowser::filtered(|p: &Path| {
            p.extension()
                .filter(|ext| ext.to_str().unwrap_or("") == "xz")
                .is_some()
                && p.file_stem()
                    .filter(|stem| {
                        Path::new(stem)
                            .extension()
                            .filter(|ext| ext.to_str().unwrap_or("") == "ko")
                            .is_some()
                    })
                    .is_some()
        })
        .with_path(Path::new("/lib/modules").join(kver).join("kernel")),
    )?
    .iter()
    .map(|path| -> Result<(String, PathBuf)> {
        Ok((
            get_module_name(
                path.as_os_str()
                    .to_str()
                    .with_context(|| "unable to convert path to string")?,
            )?,
            path.clone(),
        ))
    })
    .collect::<Result<HashMap<String, PathBuf>>>()?)
}

fn get_module_name(filename: &str) -> Result<String> {
    Ok(Path::new(filename)
        .file_stem()
        .and_then(|module| std::path::Path::new(module).file_stem())
        .with_context(|| format!("failed to get module name of file {}", filename))?
        .to_str()
        .with_context(|| {
            format!(
                "failed to convert the module name in file {} from OsStr to Str",
                filename
            )
        })?
        .to_string())
}