1use std::path::{Path, PathBuf};
2#[cfg(all(feature = "tomb", target_os = "linux"))]
3use std::process::{Command, Stdio};
4
5use anyhow::Result;
6#[cfg(all(feature = "tomb", target_os = "linux"))]
7use fs_extra::dir::CopyOptions;
8use thiserror::Error;
9
10#[cfg(all(feature = "tomb", target_os = "linux"))]
12pub const SUDO_BIN: &str = crate::systemd_bin::SUDO_BIN;
13
14pub const CHOWN_BIN: &str = "chown";
16
17#[cfg(all(feature = "tomb", target_os = "linux"))]
19pub fn dir_size(path: &Path) -> Result<u64, Err> {
20 fs_extra::dir::get_size(path).map_err(Err::DirSize)
21}
22
23#[cfg(all(feature = "tomb", target_os = "linux"))]
27pub fn copy_dir_contents(from: &Path, to: &Path) -> Result<()> {
28 let mut options = CopyOptions::new();
29 options.overwrite = true;
30 options.copy_inside = true;
31 options.content_only = true;
32 Ok(fs_extra::dir::copy(from, to, &options)
33 .map(|_| ())
34 .map_err(Err::CopyDirContents)?)
35}
36
37pub fn append_file_name(path: &Path, suffix: &str) -> Result<PathBuf> {
41 Ok(path.parent().ok_or(Err::NoParent)?.join(format!(
42 "{}{}",
43 path.file_name().ok_or(Err::UnknownName)?.to_string_lossy(),
44 suffix,
45 )))
46}
47
48#[cfg(all(feature = "tomb", target_os = "linux"))]
50pub(crate) fn sudo_chown(path: &Path, uid: u32, gid: u32, recursive: bool) -> Result<()> {
51 let mut cmd = Command::new(SUDO_BIN);
53 cmd.stdin(Stdio::inherit());
54 cmd.stdout(Stdio::inherit());
55 cmd.stderr(Stdio::inherit());
56 cmd.arg("--");
57 cmd.arg(CHOWN_BIN);
58 if recursive {
59 cmd.arg("--recursive");
60 }
61 cmd.arg(format!("{uid}:{gid}"));
62 cmd.arg(path);
63
64 let status = cmd.status().map_err(Err::SudoChown)?;
66 if status.success() {
67 Ok(())
68 } else {
69 Err(Err::Status(status).into())
70 }
71}
72
73#[cfg(all(feature = "tomb", target_os = "linux"))]
75pub(crate) fn sudo_chown_current_user(path: &Path, recursive: bool) -> Result<()> {
76 sudo_chown(
77 path,
78 nix::unistd::Uid::effective().as_raw(),
79 nix::unistd::Gid::effective().as_raw(),
80 recursive,
81 )
82}
83
84#[derive(Debug, Error)]
85pub enum Err {
86 #[cfg(all(feature = "tomb", target_os = "linux"))]
87 #[error("failed to measure directory size")]
88 DirSize(#[source] fs_extra::error::Error),
89
90 #[error("failed to copy directory contents")]
91 #[cfg(all(feature = "tomb", target_os = "linux"))]
92 CopyDirContents(#[source] fs_extra::error::Error),
93
94 #[error("failed to append suffix to file path, unknown parent")]
95 NoParent,
96
97 #[error("failed to append suffix to file path, unknown name")]
98 UnknownName,
99
100 #[error("failed to invoke 'sudo chown' on path")]
101 SudoChown(std::io::Error),
102
103 #[error("system command exited with non-zero status code: {0}")]
104 Status(std::process::ExitStatus),
105}