use ic_host_artifacts::artifact::{ArtifactError, ArtifactIdentity, hash_reader, read_reader};
use std::{fs::File, path::Path};
#[cfg(unix)]
mod beneath;
#[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::hash_file_no_follow;
#[cfg(unix)]
pub use no_follow::read_file_no_follow;
#[cfg(unix)]
pub use no_follow::read_optional_file_no_follow;
#[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_reader(open_file(path, max_bytes)?, max_bytes)
}
pub fn read_file(path: &Path, max_bytes: usize) -> Result<Vec<u8>, ArtifactError> {
read_reader(open_file(path, max_bytes as u64)?, max_bytes)
}
pub fn read_opened_file(file: File, max_bytes: usize) -> Result<Vec<u8>, ArtifactError> {
check_metadata(&file.metadata()?, max_bytes as u64)?;
read_reader(file, max_bytes)
}
pub fn open_regular_file(path: &Path) -> Result<File, ArtifactError> {
open_file(path, u64::MAX)
}
fn open_file(path: &Path, limit: u64) -> Result<File, ArtifactError> {
#[cfg(unix)]
let file = open_read_only(path, false)?;
#[cfg(not(unix))]
let file = File::open(path)?;
check_metadata(&file.metadata()?, limit)?;
Ok(file)
}
#[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 check_metadata(metadata: &std::fs::Metadata, limit: u64) -> Result<(), ArtifactError> {
if !metadata.is_file() {
return Err(ArtifactError::NotRegularFile);
}
if metadata.len() > limit {
return Err(ArtifactError::LimitExceeded { limit });
}
Ok(())
}