use std::collections::HashSet;
use std::ffi::OsStr;
use std::fs::File;
use std::io::{self, BufReader, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use super::{
try_open_file, InputFeatures, InputHandle, InputOrigin, IoProvider, OpenResult, OutputHandle,
};
use crate::errors::{ErrorKind, Result};
use crate::status::StatusBackend;
pub struct FilesystemPrimaryInputIo {
path: PathBuf,
}
impl FilesystemPrimaryInputIo {
pub fn new<P: AsRef<Path>>(path: P) -> FilesystemPrimaryInputIo {
FilesystemPrimaryInputIo {
path: path.as_ref().to_owned(),
}
}
}
impl IoProvider for FilesystemPrimaryInputIo {
fn input_open_primary(&mut self, _status: &mut dyn StatusBackend) -> OpenResult<InputHandle> {
let f = match try_open_file(&self.path) {
OpenResult::Ok(f) => f,
OpenResult::NotAvailable => return OpenResult::NotAvailable,
OpenResult::Err(e) => return OpenResult::Err(e),
};
OpenResult::Ok(InputHandle::new(
OsStr::new(""),
BufReader::new(f),
InputOrigin::Filesystem,
))
}
}
pub struct FilesystemIo {
root: PathBuf,
writes_allowed: bool,
absolute_allowed: bool,
hidden_input_paths: HashSet<PathBuf>,
}
impl FilesystemIo {
pub fn new(
root: &Path,
writes_allowed: bool,
absolute_allowed: bool,
hidden_input_paths: HashSet<PathBuf>,
) -> FilesystemIo {
FilesystemIo {
root: PathBuf::from(root),
writes_allowed,
absolute_allowed,
hidden_input_paths,
}
}
fn construct_path(&mut self, name: &OsStr) -> Result<PathBuf> {
let path = Path::new(name);
if path.is_absolute() && !self.absolute_allowed {
let as_str = String::from(path.to_string_lossy());
return Err(ErrorKind::PathForbidden(as_str).into());
}
let mut combined = PathBuf::from(&self.root);
combined.push(path);
Ok(combined)
}
}
impl IoProvider for FilesystemIo {
fn output_open_name(&mut self, name: &OsStr) -> OpenResult<OutputHandle> {
if !self.writes_allowed {
return OpenResult::NotAvailable;
}
let path = match self.construct_path(name) {
Ok(p) => p,
Err(e) => return OpenResult::Err(e),
};
let f = match File::create(path) {
Ok(f) => f,
Err(e) => return OpenResult::Err(e.into()),
};
OpenResult::Ok(OutputHandle::new(name, f))
}
fn output_open_stdout(&mut self) -> OpenResult<OutputHandle> {
OpenResult::NotAvailable
}
fn input_open_name(
&mut self,
name: &OsStr,
_status: &mut dyn StatusBackend,
) -> OpenResult<InputHandle> {
let path = match self.construct_path(name) {
Ok(p) => p,
Err(e) => return OpenResult::Err(e),
};
if self.hidden_input_paths.contains(&path) {
return OpenResult::NotAvailable;
}
let f = match File::open(path) {
Ok(f) => f,
Err(e) => {
return if e.kind() == io::ErrorKind::NotFound {
OpenResult::NotAvailable
} else if let Some(libc::ENOTDIR) = e.raw_os_error() {
OpenResult::NotAvailable
} else {
OpenResult::Err(e.into())
};
}
};
OpenResult::Ok(InputHandle::new(
name,
BufReader::new(f),
InputOrigin::Filesystem,
))
}
}
impl InputFeatures for File {
fn get_size(&mut self) -> Result<usize> {
Ok(self.metadata()?.len() as usize)
}
fn get_unix_mtime(&mut self) -> Result<Option<i64>> {
let sys_time = self.metadata()?.modified()?;
let dur = sys_time.duration_since(std::time::SystemTime::UNIX_EPOCH)?;
Ok(Some(dur.as_secs() as i64))
}
fn try_seek(&mut self, pos: SeekFrom) -> Result<u64> {
Ok(self.seek(pos)?)
}
}
impl InputFeatures for BufReader<File> {
fn get_size(&mut self) -> Result<usize> {
Ok(self.get_mut().metadata()?.len() as usize)
}
fn get_unix_mtime(&mut self) -> Result<Option<i64>> {
let sys_time = self.get_mut().metadata()?.modified()?;
let dur = sys_time.duration_since(std::time::SystemTime::UNIX_EPOCH)?;
Ok(Some(dur.as_secs() as i64))
}
fn try_seek(&mut self, pos: SeekFrom) -> Result<u64> {
Ok(self.seek(pos)?)
}
}