diff --git a/src/files.rs b/src/files.rs new file mode 100644 index 0000000..33ee6b3 --- /dev/null +++ b/src/files.rs @@ -0,0 +1,228 @@ +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 +}