From 4c590f57c82fdfb28890c1fc98d005dfc00f6832 Mon Sep 17 00:00:00 2001 From: rascul Date: Sat, 24 Aug 2019 17:43:28 -0500 Subject: [PATCH] init --- src/config.rs | 31 ++++++ src/dirs.rs | 72 ++++++++++++++ src/error.rs | 49 +++++++++ src/files/mkroot_files.rs | 202 ++++++++++++++++++++++++++++++++++++++ src/files/mod.rs | 5 + src/files/os_release.rs | 25 +++++ src/main.rs | 41 ++++++++ 7 files changed, 425 insertions(+) create mode 100644 src/config.rs create mode 100644 src/dirs.rs create mode 100644 src/error.rs create mode 100644 src/files/mkroot_files.rs create mode 100644 src/files/mod.rs create mode 100644 src/files/os_release.rs create mode 100644 src/main.rs diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..fbee890 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,31 @@ +use std::path::PathBuf; + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "mkroot")] +pub struct Config { + /// OS name (for /etc/os-release) + #[structopt(long, name = "OSNAME", default_value = "mkroot")] + pub osname: String, + + /// OS version (for /etc/os-release) + #[structopt(long, name = "OSVERSION", default_value = "1.0")] + pub osversion: String, + + /// Verbose + #[structopt(short, long)] + pub verbose: bool, + + /// Path to LDD + #[structopt(long, name = "LDD", default_value = "/usr/bin/ldd", parse(from_os_str))] + pub ldd: PathBuf, + + /// Directory for root filesystem + #[structopt(required = true, name = "DIR", parse(from_os_str))] + pub root_dir: PathBuf, + + /// Files to parse + #[structopt(required = true, name = "FILES", parse(from_os_str))] + pub files: Vec, +} diff --git a/src/dirs.rs b/src/dirs.rs new file mode 100644 index 0000000..0d4db82 --- /dev/null +++ b/src/dirs.rs @@ -0,0 +1,72 @@ +use std::fs::{create_dir, read_dir}; +use std::path::PathBuf; + +use crate::config::Config; +use crate::error::*; + +static DIRS: &[&str] = &[ + "bin", "dev", "etc", "home", "lib", "lib64", "proc", "root", "run", "sbin", "sys", "tmp", + "usr", "var", "usr/bin", +]; + +pub fn check(config: &Config) -> MkrootResult<()> { + if !&config.root_dir.exists() { + return Ok(()); + } + + open(&config.root_dir, config.verbose)?; + + for dir in DIRS { + let mut d = PathBuf::from(&config.root_dir); + d.push(&dir); + open(&d, config.verbose)?; + } + + Ok(()) +} + +fn open(dir: &PathBuf, verbose: bool) -> MkrootResult<()> { + if dir.exists() { + if verbose { + println!("Checking directory {}", &dir.display()); + } + if let Err(e) = read_dir(&dir) { + return Err(MkrootError::from(format!( + "Error opening directory {}: {}", + &dir.display(), + e + ))); + } + } + + Ok(()) +} + +pub fn create(config: &Config) -> MkrootResult<()> { + mkdir(&config.root_dir, config.verbose)?; + + for dir in DIRS { + let mut d = PathBuf::from(&config.root_dir); + d.push(&dir); + mkdir(&d, config.verbose)?; + } + + Ok(()) +} + +fn mkdir(dir: &PathBuf, verbose: bool) -> MkrootResult<()> { + if !dir.exists() { + if verbose { + println!("Creating directory {}", &dir.display()); + } + if let Err(e) = create_dir(&dir) { + return Err(MkrootError::from(format!( + "Error creating directory {}: {}", + &dir.display(), + e + ))); + } + } + + Ok(()) +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..558249e --- /dev/null +++ b/src/error.rs @@ -0,0 +1,49 @@ +use std::fmt::{Display, Formatter, Result as FmtResult}; +use std::io::Error as IoError; + +use regex::Error as RegexError; + +pub type MkrootResult = Result; + +#[derive(Debug)] +pub enum MkrootError { + Io(IoError), + Regex(RegexError), + Custom(String), + Empty, +} + +impl From for MkrootError { + fn from(e: IoError) -> MkrootError { + MkrootError::Io(e) + } +} + +impl From for MkrootError { + fn from(e: RegexError) -> MkrootError { + MkrootError::Regex(e) + } +} + +impl From for MkrootError { + fn from(e: String) -> MkrootError { + MkrootError::Custom(e) + } +} + +impl From<()> for MkrootError { + fn from(_: ()) -> MkrootError { + MkrootError::Empty + } +} + +impl Display for MkrootError { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + match *self { + MkrootError::Io(ref e) => Display::fmt(e, f), + MkrootError::Regex(ref e) => Display::fmt(e, f), + MkrootError::Custom(ref e) => Display::fmt(e, f), + MkrootError::Empty => Display::fmt("Empty", f), + } + } +} diff --git a/src/files/mkroot_files.rs b/src/files/mkroot_files.rs new file mode 100644 index 0000000..e8ff03b --- /dev/null +++ b/src/files/mkroot_files.rs @@ -0,0 +1,202 @@ +use std::fs::{copy as fscopy, File}; +use std::path::PathBuf; +use std::process::Command; + +use regex::Regex; + +use crate::config::Config; +use crate::error::*; + +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()); + } + + Files::open(&file)?; + + let (libs, lib64s) = Files::libs_from_ldd(&file, &config)?; + myfiles.libs.extend(libs); + myfiles.lib64s.extend(lib64s); + + if Files::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) + } + + 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 = Files::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 Files::check_lib64(&match_path) { + lib64s.push(match_path); + } else { + libs.push(match_path); + } + } + } + } + + Ok((libs, lib64s)) + } + + 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 + } + + 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 + ))), + } + } + + pub fn copy(&self, config: &Config) -> MkrootResult<()> { + let mut target = PathBuf::from(&config.root_dir); + target.push("bin"); + Files::copy_files(&self.bins, &target, config.verbose)?; + + let mut target = PathBuf::from(&config.root_dir); + target.push("sbin"); + Files::copy_files(&self.sbins, &target, config.verbose)?; + + let mut target = PathBuf::from(&config.root_dir); + target.push("libs"); + Files::copy_files(&self.libs, &target, config.verbose)?; + + let mut target = PathBuf::from(&config.root_dir); + target.push("lib64"); + Files::copy_files(&self.lib64s, &target, config.verbose)?; + + Ok(()) + } + + fn copy_files(files: &[PathBuf], target: &PathBuf, 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()); + } + + if let Err(e) = fscopy(&f, &t) { + return Err(MkrootError::from(format!( + "Error copying file from {} to {}: {}", + &f.display(), + &t.display(), + e + ))); + } + } else if verbose { + println!("Skipping {}", &f.display()); + } + } + + Ok(()) + } +} diff --git a/src/files/mod.rs b/src/files/mod.rs new file mode 100644 index 0000000..eeff5fd --- /dev/null +++ b/src/files/mod.rs @@ -0,0 +1,5 @@ +mod mkroot_files; +mod os_release; + +pub use mkroot_files::Files; +pub use os_release::os_release; diff --git a/src/files/os_release.rs b/src/files/os_release.rs new file mode 100644 index 0000000..9704f98 --- /dev/null +++ b/src/files/os_release.rs @@ -0,0 +1,25 @@ +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::PathBuf; + +use crate::config::Config; +use crate::error::MkrootResult; + +pub fn os_release(config: &Config) -> MkrootResult<()> { + if config.verbose { + println!("Creating file etc/os-release"); + } + + let mut path = PathBuf::from(&config.root_dir); + path.push("etc/os-release"); + + let f = File::create(&path)?; + let mut writer = BufWriter::new(f); + + writer.write_fmt(format_args!( + "NAME=\"{}\"\nVERSION=\"{}\"\n", + &config.osname, &config.osversion + ))?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e555c7a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,41 @@ +extern crate regex; +extern crate structopt; + +mod config; +mod dirs; +mod error; +mod files; + +use structopt::StructOpt; + +use config::Config; + +fn main() -> error::MkrootResult<()> { + let config: Config = StructOpt::from_args(); + + if config.verbose { + println!( + "Building root fs for {:?} in {}", + &config.files, + &config.root_dir.display() + ); + } + + dirs::check(&config)?; + let files = files::Files::gather(&config)?; + dirs::create(&config)?; + files.copy(&config)?; + + if let Err(e) = files::os_release(&config) { + return Err(error::MkrootError::from(format!( + "Error creating etc/os-release: {}", + e + ))); + } + + // cp skel + // run ldconfig + // set permissions + + Ok(()) +}