use std::fs::{self, File};
use std::io::{self, Read};
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use flate2::read::GzDecoder;
use tempfile::TempDir;
use tracing::trace;
use crate::error::Error;
pub(crate) fn read_to_bytes<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let mut buf = Vec::new();
let mut file = File::open(path)?;
file.read_to_end(&mut buf)?;
Ok(buf)
}
pub(crate) fn file_mode<P: AsRef<Path>>(path: P) -> io::Result<String> {
trace!("Determining file mode: {}", path.as_ref().to_string_lossy());
let mode = File::open(path)?.metadata()?.permissions().mode() & 0o777;
Ok(format!("{mode:03o}"))
}
pub(crate) fn unpack_tar_gz<P: AsRef<Path>>(path: P) -> io::Result<TempDir> {
trace!("Unpacking tar/gz file: {}", path.as_ref().to_string_lossy());
let file = File::open(path)?;
let ungz = GzDecoder::new(file);
let mut archive = tar::Archive::new(ungz);
let out = TempDir::new()?;
trace!("Unpacking tar/gz file into: {}", out.path().to_string_lossy());
archive.unpack(&out)?;
Ok(out)
}
#[derive(Debug, Default)]
pub(crate) struct DirectoryContents {
pub files: Vec<PathBuf>,
pub outside_base: Vec<PathBuf>,
pub broken_links: Vec<PathBuf>,
}
pub(crate) fn get_contents<P: AsRef<Path>>(path: P, root: P) -> Result<DirectoryContents, Error> {
trace!(
"Listing contents of directory tree: {}",
path.as_ref().to_string_lossy()
);
let wd = walkdir::WalkDir::new(&path).follow_links(true).sort_by_file_name();
let mut dc = DirectoryContents::default();
for result in wd {
match result {
Ok(entry) => {
if entry.path_is_symlink() {
match fs::canonicalize(entry.path()) {
Ok(real_path) => {
if real_path.is_dir() {
continue;
}
if real_path.strip_prefix(&root).is_ok() {
dc.files.push(
entry
.path()
.strip_prefix(&path)
.map_err(|err| Error::Walk { inner: err.to_string() })?
.to_owned(),
);
} else {
dc.outside_base.push(entry.path().read_link()?);
}
},
Err(err) => return Err(err.into()),
}
} else {
if entry.file_type().is_dir() {
continue;
}
let file = entry
.path()
.strip_prefix(&path)
.map_err(|err| Error::Walk { inner: err.to_string() })?;
dc.files.push(file.to_owned());
}
},
Err(err) => {
if let Some(file_path) = err.path() {
if let Ok(rel_path) = file_path.strip_prefix(&root) {
dc.broken_links.push(rel_path.to_owned());
} else {
dc.outside_base.push(file_path.to_owned());
}
} else {
return Err(Error::Walk { inner: err.to_string() });
}
},
}
}
Ok(dc)
}