redox_installer 0.2.42

A Redox filesystem builder
Documentation
use anyhow::{Context, Result};
use libc::{gid_t, uid_t};

use std::ffi::{CString, OsStr};
use std::fs::{self, File};
use std::io::{Error, Write};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{symlink, PermissionsExt};
use std::path::Path;

fn chown<P: AsRef<Path>>(path: P, uid: uid_t, gid: gid_t, recursive: bool) -> Result<()> {
    let path = path.as_ref();

    let c_path = CString::new(path.as_os_str().as_bytes()).unwrap();
    if unsafe { libc::chown(c_path.as_ptr(), uid, gid) } != 0 {
        return Err(Error::last_os_error().into());
    }

    if recursive && path.is_dir() {
        for entry_res in fs::read_dir(path)? {
            let entry = entry_res?;
            chown(entry.path(), uid, gid, recursive)?;
        }
    }

    Ok(())
}

// TODO: Rewrite impls
impl crate::FileConfig {
    pub(crate) fn create<P: AsRef<Path>>(&self, prefix: P) -> Result<()> {
        let path = self.path.trim_start_matches('/');
        let target_file = prefix.as_ref().join(path);

        if self.directory {
            println!("Create directory {}", target_file.display());
            fs::create_dir_all(&target_file)
                .with_context(|| format!("failed to create directory {}", target_file.display()))?;
            self.apply_perms(&target_file)?;
            return Ok(());
        } else if let Some(parent) = target_file.parent() {
            println!("Create file parent {}", parent.display());
            fs::create_dir_all(parent)
                .with_context(|| format!("failed to create file parent {}", parent.display()))?;
        }

        if self.symlink {
            println!("Create symlink {} to {}", target_file.display(), self.data);
            if target_file.is_symlink() {
                fs::remove_file(&target_file).with_context(|| {
                    format!("failed to remove old symlink {}", target_file.display())
                })?;
            }
            symlink(&OsStr::new(&self.data), &target_file).with_context(|| {
                format!(
                    "failed to create symlink {} to {}",
                    target_file.display(),
                    self.data
                )
            })?;
            Ok(())
        } else {
            println!("Create file {}", target_file.display());
            let mut file = File::create(&target_file)
                .with_context(|| format!("failed to create file {}", target_file.display()))?;
            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_else(|| if self.directory { 0o0755 } else { 0o0644 });
        let uid = self.uid.unwrap_or(!0);
        let gid = self.gid.unwrap_or(!0);

        // chmod
        fs::set_permissions(path, fs::Permissions::from_mode(mode))
            .with_context(|| format!("failed to set permissions on {}", path.display()))?;

        // chown
        chown(path, uid, gid, self.recursive_chown)
            .with_context(|| format!("failed to chown {}", path.display()))
    }
}