~danyspin97/initrz

ref: 909859e0cfcb8262668340ab458ceaabc0d8937d initrz/src/initrz/src/module_loader.rs -rw-r--r-- 3.8 KiB
909859e0Danilo Spinella mkinitrz: Read modules from modules.dep 6 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use anyhow::{bail, Context, Result};
use dowser::Dowser;
use glob::{glob, Pattern};
use log::{debug, warn};
use nix::kmod::{init_module, ModuleInitFlags};
use xz2::bufread::XzDecoder;

use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::ffi::CString;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::mem::drop;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::sync::RwLock;

use common::Modules;

pub struct ModAlias {
    pattern: Pattern,
    module: String,
}

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

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

    Ok(lines
        .filter_map(|line: Result<String, _>| line.ok())
        .map(|line| -> Result<ModAlias> {
            let mut split = line[6..].splitn(2, ' ');
            Ok(ModAlias {
                pattern: Pattern::new(split.next().with_context(|| "no pattern found")?)?,
                module: split
                    .next()
                    .with_context(|| "no modalias found")?
                    .to_string(),
            })
        })
        .filter_map(|res| {
            if res.is_err() {
                warn!("unable to parse modalias line");
            }
            res.ok()
        })
        .collect())
}

impl ModuleLoader {
    pub fn init(kernel_version: &str) -> Result<ModuleLoader> {
        let kernel_root = Path::new("/lib/modules").join(kernel_version);
        let mut modules = HashSet::new();

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

    pub fn load_module(&self, module_name: &str) -> Result<bool> {
        let modules_loaded = self.modules_loaded.read().unwrap();
        if !modules_loaded.contains(module_name) {
            drop(modules_loaded);
            debug!("loading module {}", module_name);
            let module = self.modules.get(module_name);
            if module.is_none() {
                return Ok(false);
            }
            let module = module.unwrap();
            // Some modules could be builtin, do not block
            module.deps.iter().try_for_each(|dep| -> Result<()> {
                self.load_module(&dep)?;
                Ok(())
            })?;
            let mut modules_loaded = self.modules_loaded.write().unwrap();
            if !modules_loaded.contains(module_name) {
                modules_loaded.insert(String::from(module_name));
            }
            // unlock so that other modules can be loaded in parallel
            drop(modules_loaded);
            let filename = self.kernel_root.join(&module.filename);
            let module_file =
                File::open(&filename).with_context(|| format!("unable to find {:?}", filename))?;
            let mut buf = Vec::new();
            XzDecoder::new(BufReader::new(module_file)).read_to_end(&mut buf)?;

            init_module(&buf, &CString::new("")?).with_context(|| {
                format!("finit_module call failed when loading {}", module_name)
            })?;
        }

        Ok(true)
    }

    pub fn load_modalias(&self, modalias: &str) -> Result<()> {
        let modalias = &self.aliases.iter().find(|m| m.pattern.matches(modalias));
        if let Some(modalias) = modalias {
            self.load_module(&modalias.module)?;
        }

        Ok(())
    }
}