use std::path::{Path, PathBuf};
#[cfg(all(feature = "tomb", target_os = "linux"))]
use std::process::{Command, Stdio};
use anyhow::Result;
#[cfg(all(feature = "tomb", target_os = "linux"))]
use fs_extra::dir::CopyOptions;
use thiserror::Error;
#[cfg(all(feature = "tomb", target_os = "linux"))]
pub const SUDO_BIN: &str = crate::systemd_bin::SUDO_BIN;
pub const CHOWN_BIN: &str = "chown";
#[cfg(all(feature = "tomb", target_os = "linux"))]
pub fn dir_size(path: &Path) -> Result<u64, Err> {
fs_extra::dir::get_size(path).map_err(Err::DirSize)
}
#[cfg(all(feature = "tomb", target_os = "linux"))]
pub fn copy_dir_contents(from: &Path, to: &Path) -> Result<()> {
let mut options = CopyOptions::new();
options.overwrite = true;
options.copy_inside = true;
options.content_only = true;
Ok(fs_extra::dir::copy(from, to, &options)
.map(|_| ())
.map_err(Err::CopyDirContents)?)
}
pub fn append_file_name(path: &Path, suffix: &str) -> Result<PathBuf> {
Ok(path.parent().ok_or(Err::NoParent)?.join(format!(
"{}{}",
path.file_name().ok_or(Err::UnknownName)?.to_string_lossy(),
suffix,
)))
}
#[cfg(all(feature = "tomb", target_os = "linux"))]
pub(crate) fn sudo_chown(path: &Path, uid: u32, gid: u32, recursive: bool) -> Result<()> {
let mut cmd = Command::new(SUDO_BIN);
cmd.stdin(Stdio::inherit());
cmd.stdout(Stdio::inherit());
cmd.stderr(Stdio::inherit());
cmd.arg("--");
cmd.arg(CHOWN_BIN);
if recursive {
cmd.arg("--recursive");
}
cmd.arg(format!("{}:{}", uid, gid));
cmd.arg(path);
let status = cmd.status().map_err(Err::SudoChown)?;
if status.success() {
Ok(())
} else {
Err(Err::Status(status).into())
}
}
#[cfg(all(feature = "tomb", target_os = "linux"))]
pub(crate) fn sudo_chown_current_user(path: &Path, recursive: bool) -> Result<()> {
sudo_chown(
path,
nix::unistd::Uid::effective().as_raw(),
nix::unistd::Gid::effective().as_raw(),
recursive,
)
}
#[derive(Debug, Error)]
pub enum Err {
#[cfg(all(feature = "tomb", target_os = "linux"))]
#[error("failed to measure directory size")]
DirSize(#[source] fs_extra::error::Error),
#[error("failed to copy directory contents")]
#[cfg(all(feature = "tomb", target_os = "linux"))]
CopyDirContents(#[source] fs_extra::error::Error),
#[error("failed to append suffix to file path, unknown parent")]
NoParent,
#[error("failed to append suffix to file path, unknown name")]
UnknownName,
#[error("failed to invoke 'sudo chown' on path")]
SudoChown(std::io::Error),
#[error("system command exited with non-zero status code: {0}")]
Status(std::process::ExitStatus),
}