use std::fs::{File, Permissions}; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; use std::process::Command; use regex::Regex; use crate::config::Config; use crate::error::*; use crate::util::{copy_file, set_perms}; pub struct Files { pub bins: Vec, pub sbins: Vec, pub libs: Vec, pub lib64s: Vec, } impl Files { pub fn gather(config: &Config) -> MkrootResult { let mut myfiles = Files { bins: Vec::new(), sbins: Vec::new(), libs: Vec::new(), lib64s: Vec::new(), }; for file in &config.files { if config.verbose { println!("Checking {}", &file.display()); } open(&file)?; let (libs, lib64s) = libs_from_ldd(&file, &config)?; myfiles.libs.extend(libs); myfiles.lib64s.extend(lib64s); if check_sbin(&file) { myfiles.sbins.push(file.to_owned()); } else { myfiles.bins.push(file.to_owned()); } } myfiles.bins.sort(); myfiles.bins.dedup(); myfiles.sbins.sort(); myfiles.sbins.dedup(); myfiles.libs.sort(); myfiles.libs.dedup(); myfiles.lib64s.sort(); myfiles.lib64s.dedup(); Ok(myfiles) } pub fn copy(&self, config: &Config) -> MkrootResult<()> { let mut target = PathBuf::from(&config.root_dir); target.push("bin"); copy_files(&self.bins, &target, 0o755, config.verbose)?; let mut target = PathBuf::from(&config.root_dir); target.push("sbin"); copy_files(&self.sbins, &target, 0o755, config.verbose)?; let mut target = PathBuf::from(&config.root_dir); target.push("libs"); copy_files(&self.libs, &target, 0o644, config.verbose)?; let mut target = PathBuf::from(&config.root_dir); target.push("lib64"); copy_files(&self.lib64s, &target, 0o644, config.verbose)?; Ok(()) } } pub fn set_linker_permissions( libs: &[PathBuf], dir: &PathBuf, verbose: bool, ) -> MkrootResult<()> { for lib in libs { if let Some(fn_osstr) = &lib.file_name() { if let Some(fn_str) = fn_osstr.to_str() { if fn_str.starts_with("ld-linux") { let mut lib = PathBuf::from(&dir); lib.push(&fn_str); if verbose { println!("Setting linker {} to mode 0o755", &lib.display()); } set_perms(&lib, Permissions::from_mode(0o755))?; } } } } Ok(()) } fn open(p: &PathBuf) -> MkrootResult<()> { if let Err(e) = File::open(&p) { return Err(MkrootError::from(format!( "Error opening file ({}): {}", &p.display(), e ))); } Ok(()) } fn libs_from_ldd( file: &PathBuf, config: &Config, ) -> MkrootResult<(Vec, Vec)> { let ldd = ldd(&file, &config)?; let mut libs: Vec = Vec::new(); let mut lib64s: Vec = Vec::new(); if ldd.is_empty() { return Ok((libs, lib64s)); } let re = Regex::new(r"(^|.* )(?P/.*) \(0x[[:xdigit:]]{16}\)$")?; for line in ldd.lines() { let line = String::from(line.trim()); if let Some(caps) = re.captures(&line) { if let Some(rematch) = caps.name("path") { let match_path = PathBuf::from(rematch.as_str()); if config.verbose { println!("Adding {}", &match_path.display()); } if check_lib64(&match_path) { lib64s.push(match_path); } else { libs.push(match_path); } } } } Ok((libs, lib64s)) } fn copy_files( files: &[PathBuf], target: &PathBuf, mode: u32, verbose: bool, ) -> MkrootResult<()> { for f in files { let mut t = PathBuf::from(&target); if let Some(filename) = &f.file_name() { t.push(filename); if verbose { println!("Copying {} to {}", &f.display(), &t.display()); } copy_file(&f, &t)?; set_perms(&t, Permissions::from_mode(mode))?; } else if verbose { println!("Skipping {}", &f.display()); } } Ok(()) } fn ldd(file: &PathBuf, config: &Config) -> MkrootResult { match Command::new(&config.ldd).arg(file).output() { Ok(output) => { if output.status.success() { Ok(String::from_utf8(output.stdout).unwrap_or_default()) } else if config.verbose { let mut out = String::from("ldd failed: "); let stdout = String::from_utf8(output.stdout).unwrap_or_default(); let stderr = String::from_utf8(output.stderr).unwrap_or_default(); if !stdout.is_empty() { out = out + "stdout: " + &stdout + " "; } if !stderr.is_empty() { out = out + "stderr: " + &stderr; } println!("{}", out.trim()); Ok(String::new()) } else { Ok(String::new()) } } Err(e) => Err(MkrootError::from(format!( "Error running ldd ({}): {}", &config.ldd.display(), e ))), } } fn check_lib64(path: &PathBuf) -> bool { for c in path.components() { if c.as_os_str() == "lib64" { return true; } } false } fn check_sbin(path: &PathBuf) -> bool { for c in path.components() { if c.as_os_str() == "sbin" { return true; } } false }