diff --git a/Cargo.lock b/Cargo.lock index 8eb6809bf635d20d7cf979f36ccea35e5d029897..811650c2c95bfa65a13b50cecd06313e00c870ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -438,6 +438,7 @@ dependencies = [ "arg_parser 0.1.0 (git+https://github.com/redox-os/arg-parser.git)", "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "liner 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "pkgutils 0.1.1 (git+https://github.com/redox-os/pkgutils.git)", "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index b873ce4b32dbcb8306088436524eeac70d694684..3b789ff5af75f6c475d5a8d1fe567fc841e8024a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ path = "src/lib.rs" arg_parser = { git = "https://github.com/redox-os/arg-parser.git" } argon2rs = { version = "0.2", default-features = false } liner = "0.1" +libc = "0.2" failure = "0.1" pkgutils = { git = "https://github.com/redox-os/pkgutils.git" } rand = "0.3" diff --git a/src/config/file.rs b/src/config/file.rs index 86b491e6554ccf9793285d4d0fe727ea4b9b45b0..2104fdfe970d616d165984f56aaec1c9dbd7ee8e 100644 --- a/src/config/file.rs +++ b/src/config/file.rs @@ -1,7 +1,74 @@ + +use Result; +use libc::chown; + +use std::io::{Error, Write}; +use std::ffi::{CString, OsStr}; +use std::fs::{self, File}; +use std::os::unix::ffi::OsStrExt; +use std::os::unix::fs::{PermissionsExt, symlink}; +use std::path::Path; + +//type Result<T> = std::result::Result<T, Error>; + #[derive(Debug, Default, Deserialize)] pub struct FileConfig { pub path: String, pub data: String, #[serde(default)] - pub symlink: bool + pub symlink: bool, + pub mode: Option<u32>, + pub uid: Option<u32>, + pub gid: Option<u32> +} + +// TODO: Rewrite +impl FileConfig { + + pub(crate) fn create<P: AsRef<Path>>(self, prefix: P) -> Result<()> { + let path = self.path.trim_left_matches('/'); + let target_file = prefix.as_ref() + .join(path); + + println!("target file: {:?}", target_file); + + if let Some(parent) = target_file.parent() { + println!("Create file parent {}", parent.display()); + fs::create_dir_all(parent)?; + } + + if self.symlink { + println!("Create symlink {}", target_file.display()); + symlink(&OsStr::new(&self.data), &target_file)?; + Ok(()) + } else { + println!("Create file {}", target_file.display()); + let mut file = File::create(&target_file)?; + file.write_all(self.data.as_bytes())?; + + self.apply_perms(target_file) + } + } + + fn apply_perms<P: AsRef<Path>>(&self, target: P) -> Result<()> { + let path = target.as_ref(); + let mode = self.mode.unwrap_or(0o0755); + let uid = self.uid.unwrap_or(0); + let gid = self.gid.unwrap_or(0); + + // chmod + fs::set_permissions(path, fs::Permissions::from_mode(mode))?; + + // chown + let c_path = CString::new(path.as_os_str().as_bytes()).unwrap(); + let ret = unsafe { + chown(c_path.as_ptr(), uid, gid) + }; + // credit to uutils + if ret == 0 { + Ok(()) + } else { + Err(Error::last_os_error().into()) + } + } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 5465d4f9f896ff4a93d43abba76123c3c9874527..4d302620e1f9c42a7d148c387ef5574718d4afa9 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; mod general; -mod file; +pub(crate) mod file; mod package; mod user; diff --git a/src/lib.rs b/src/lib.rs index a34ae1de48c7464da5427057dcbd6d0181fd16a2..ed122a87f0e9f905a16be6f7a530010e43e8edb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ #[macro_use] extern crate serde_derive; extern crate argon2rs; +extern crate libc; extern crate liner; extern crate failure; extern crate pkgutils; @@ -12,6 +13,7 @@ extern crate termion; mod config; pub use config::Config; +use config::file::FileConfig; use argon2rs::verifier::Encoded; use argon2rs::{Argon2, Variant}; @@ -21,15 +23,12 @@ use termion::input::TermRead; use pkgutils::{Repo, Package}; use std::{env, fs}; -use std::ffi::OsStr; use std::io::{self, stderr, Write}; -use std::os::unix::ffi::OsStrExt; -use std::os::unix::fs::symlink; use std::path::Path; use std::process::{self, Command}; use std::str::FromStr; -type Result<T> = std::result::Result<T, Error>; +pub(crate) type Result<T> = std::result::Result<T, Error>; const REMOTE: &'static str = "https://static.redox-os.org/pkg"; const TARGET: &'static str = "x86_64-unknown-redox"; @@ -133,28 +132,6 @@ pub fn install<P: AsRef<Path>, S: AsRef<str>>(config: Config, output_dir: P, coo Ok(()) } - /// Creates a file relative to the output directory - fn create_file_relative<P: AsRef<Path>>(path: P, data: &[u8], is_symlink: bool) -> Result<()> { - let root = env::var(SYSROOT)?; - let target_file = Path::new(&root) - .join(path.as_ref()); - - if let Some(parent) = target_file.parent() { - println!("Create file parent {}", parent.display()); - fs::create_dir_all(parent)?; - } - - if is_symlink { - println!("Create symlink {}", target_file.display()); - symlink(&OsStr::from_bytes(data), &target_file)?; - } else { - println!("Create file {}", target_file.display()); - let mut file = fs::File::create(&target_file)?; - file.write_all(data)?; - } - Ok(()) - } - let mut context = liner::Context::new(); macro_rules! prompt { @@ -172,22 +149,21 @@ pub fn install<P: AsRef<Path>, S: AsRef<str>>(config: Config, output_dir: P, coo }) } + let output_dir = output_dir.as_ref(); + // Using an env var here to communicate the root dir to the functions // instead of passing it as a param - env::set_var(SYSROOT, output_dir.as_ref().as_os_str()); + env::set_var(SYSROOT, output_dir.as_os_str()); - let output_dir = output_dir.as_ref(); println!("Install {:#?} to {}", config, output_dir.display()); // TODO: Mount disk if output is a file let output_dir = output_dir.to_owned(); - create_dir_relative("")?; - install_packages(&config, output_dir.to_str().unwrap(), cookbook); for file in config.files { - create_file_relative(file.path.trim_matches('/'), file.data.as_bytes(), file.symlink)?; + file.create(&output_dir)?; } let mut passwd = String::new(); @@ -233,7 +209,14 @@ pub fn install<P: AsRef<Path>, S: AsRef<str>>(config: Config, output_dir: P, coo } if !passwd.is_empty() { - create_file_relative("etc/passwd", passwd.as_bytes(), false)?; + FileConfig { + path: "/etc/passwd".to_string(), + data: passwd, + symlink: false, + mode: Some(0o755), + uid: Some(0), + gid: Some(0) + }.create(output_dir)?; } Ok(())