use ic_host_artifacts::artifact::{ArtifactError, ArtifactIdentity, hash_reader, read_reader};
use std::{
fs::{File, Metadata},
io::Read as _,
path::Path,
};
mod size;
pub use size::directory_logical_size;
#[cfg(unix)]
mod beneath;
#[cfg(unix)]
mod entry;
#[cfg(unix)]
pub use beneath::{
OpenEntryError, OpenedEntry, open_directory_no_follow, open_entry_beneath,
open_regular_file_beneath,
};
#[cfg(unix)]
mod no_follow;
#[cfg(unix)]
pub mod tree;
#[cfg(unix)]
pub use no_follow::read_file_no_follow;
#[cfg(unix)]
pub use no_follow::read_optional_file_no_follow;
#[cfg(unix)]
pub use no_follow::{hash_file_no_follow, hash_file_no_follow_with_metadata};
#[cfg(unix)]
mod private;
#[cfg(unix)]
pub use private::{PrivateFileReadError, read_private_bytes};
#[cfg(test)]
mod tests;
pub fn hash_file(path: &Path, max_bytes: u64) -> Result<ArtifactIdentity, ArtifactError> {
hash_file_with_metadata(path, max_bytes).map(|(identity, _)| identity)
}
pub fn hash_file_with_metadata(
path: &Path,
max_bytes: u64,
) -> Result<(ArtifactIdentity, Metadata), ArtifactError> {
let (file, metadata) = open_file(path, max_bytes)?;
Ok((hash_reader(file, max_bytes)?, metadata))
}
pub fn read_file(path: &Path, max_bytes: usize) -> Result<Vec<u8>, ArtifactError> {
read_reader(open_file(path, max_bytes as u64)?.0, max_bytes)
}
pub fn read_file_prefix(path: &Path, max_bytes: usize) -> Result<(Vec<u8>, u64), ArtifactError> {
let (file, metadata) = open_file(path, u64::MAX)?;
let bytes = read_reader(file.take(max_bytes as u64), max_bytes)?;
Ok((bytes, metadata.len()))
}
pub fn read_opened_file(file: File, max_bytes: usize) -> Result<Vec<u8>, ArtifactError> {
admit_file(&file, max_bytes as u64)?;
read_reader(file, max_bytes)
}
pub fn open_regular_file(path: &Path) -> Result<File, ArtifactError> {
Ok(open_file(path, u64::MAX)?.0)
}
fn open_file(path: &Path, limit: u64) -> Result<(File, Metadata), ArtifactError> {
#[cfg(unix)]
let file = open_read_only(path, false)?;
#[cfg(not(unix))]
let file = File::open(path)?;
let metadata = admit_file(&file, limit)?;
Ok((file, metadata))
}
#[cfg(unix)]
fn open_read_only(path: &Path, no_follow: bool) -> Result<File, ArtifactError> {
use rustix::fs::{Mode, OFlags, open};
let mut flags = OFlags::RDONLY | OFlags::NONBLOCK | OFlags::CLOEXEC;
if no_follow {
flags |= OFlags::NOFOLLOW;
}
Ok(File::from(
open(path, flags, Mode::empty()).map_err(std::io::Error::from)?,
))
}
fn admit_file(file: &File, limit: u64) -> Result<Metadata, ArtifactError> {
let metadata = file.metadata()?;
if !metadata.is_file() {
return Err(ArtifactError::NotRegularFile);
}
if metadata.len() > limit {
return Err(ArtifactError::LimitExceeded { limit });
}
Ok(metadata)
}