use super::*;
use std::io::{Read, Seek, SeekFrom, Write};
impl<F: Read + Seek> HostFileRead for F {
fn remaining_len(&mut self) -> Result<u64, IoError> {
let cur_pos = self.stream_position().map_err(|_| IoError)?;
let end_pos = self.seek(SeekFrom::End(0)).map_err(|_| IoError)?;
if cur_pos != end_pos {
self.seek(SeekFrom::Start(cur_pos)).map_err(|_| IoError)?;
}
Ok(end_pos - cur_pos)
}
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), IoError> {
Read::read_exact(self, buf).map_err(|_| IoError)
}
}
impl<W: Write> HostFileWrite for W {
fn write_all(&mut self, buf: &[u8]) -> Result<(), IoError> {
Write::write_all(self, buf).map_err(|_| IoError)
}
}
pub struct StdDirReader {
path: std::path::PathBuf,
read_dir: std::fs::ReadDir,
}
impl StdDirReader {
pub fn new(path: std::path::PathBuf) -> Result<Self, IoError> {
let read_dir = std::fs::read_dir(&path).map_err(|_| IoError)?;
Ok(Self { read_dir, path })
}
pub fn path(&self) -> &std::path::Path {
&self.path
}
}
impl HostDirRead<std::fs::File> for StdDirReader {
type Id = (u64, u64);
fn next_entry(&mut self) -> Option<Result<HostDirEntry, IoError>> {
loop {
let Ok(entry) = self.read_dir.next()? else {
return Some(Err(IoError));
};
let Ok(file_type) = entry.file_type() else {
return Some(Err(IoError));
};
let kind = if file_type.is_dir() {
NodeKind::Dir
} else if file_type.is_file() {
NodeKind::File
} else {
log::debug!("Skipping {:?}: unsupported file type {file_type:?}", entry.path());
continue;
};
let Ok(file_name) = os_string_to_c_string(entry.file_name()) else {
return Some(Err(IoError));
};
let file_name = FileName(file_name);
return Some(Ok(HostDirEntry { kind, file_name }));
}
}
fn open_file(&mut self, name: &FileName) -> Result<std::fs::File, IoError> {
let name = c_str_to_os_str(name.0.as_c_str()).map_err(|_| IoError)?;
let path = self.path.join(name);
std::fs::File::open(&path).map_err(|_| IoError)
}
fn open_dir(&mut self, name: &FileName) -> Result<(Self, Option<Self::Id>), IoError> {
let name = c_str_to_os_str(name.0.as_c_str()).map_err(|_| IoError)?;
let path = self.path.join(name);
let dir_id = get_file_id(&path)?;
let dir = Self::new(path)?;
Ok((dir, dir_id))
}
}
impl FileName {
pub fn as_os_str(&self) -> Result<&std::ffi::OsStr, InvalidPath> {
c_str_to_os_str(self.0.as_c_str())
}
pub fn into_os_string(self) -> Result<std::ffi::OsString, InvalidPath> {
c_string_to_os_string(self.0)
}
}
#[cfg(unix)]
fn get_file_id(path: &std::path::Path) -> Result<Option<(u64, u64)>, IoError> {
use crate::std::os::unix::fs::MetadataExt;
let meta = std::fs::metadata(path).map_err(|_| IoError)?;
let id = (meta.dev(), meta.ino());
Ok(Some(id))
}
#[cfg(not(unix))]
fn get_file_id(_path: &std::path::Path) -> Result<Option<(u64, u64)>, IoError> {
Ok(None)
}
#[cfg(unix)]
fn os_string_to_c_string(file_name: std::ffi::OsString) -> Result<CString, InvalidPath> {
use std::os::unix::ffi::OsStringExt;
let mut bytes = file_name.into_vec();
bytes.push(0_u8);
Ok(CString::from_vec_with_nul(bytes)
.expect("There are no interior NUL bytes on Unix, we added a NUL byte at the end"))
}
#[cfg(not(unix))]
fn os_string_to_c_string(file_name: std::ffi::OsString) -> Result<CString, InvalidPath> {
let Ok(string) = file_name.into_string() else {
return Err(InvalidPath);
};
let mut bytes: Vec<u8> = string.into();
bytes.push(0_u8);
CString::from_vec_with_nul(bytes).map_err(|_| InvalidPath)
}
#[cfg(unix)]
fn c_str_to_os_str(file_name: &CStr) -> Result<&std::ffi::OsStr, InvalidPath> {
use std::os::unix::ffi::OsStrExt;
Ok(std::ffi::OsStr::from_bytes(file_name.to_bytes()))
}
#[cfg(not(unix))]
fn c_str_to_os_str(file_name: &CStr) -> Result<&std::ffi::OsStr, InvalidPath> {
let Ok(name) = core::str::from_utf8(file_name.to_bytes()) else {
return Err(InvalidPath);
};
Ok(std::ffi::OsStr::new(name))
}
#[cfg(unix)]
fn c_string_to_os_string(file_name: CString) -> Result<std::ffi::OsString, InvalidPath> {
use std::os::unix::ffi::OsStringExt;
Ok(std::ffi::OsString::from_vec(file_name.into_bytes()))
}
#[cfg(not(unix))]
fn c_string_to_os_string(file_name: CString) -> Result<std::ffi::OsString, InvalidPath> {
let Ok(name) = alloc::string::String::from_utf8(file_name.into_bytes()) else {
return Err(InvalidPath);
};
Ok(name.into())
}
pub struct StdDirWriter {
path: std::path::PathBuf,
dir_created: bool,
}
impl StdDirWriter {
pub fn new(path: std::path::PathBuf) -> Self {
Self { path, dir_created: false }
}
pub fn path(&self) -> &std::path::Path {
&self.path
}
}
impl HostDirWrite for StdDirWriter {
type FileWrite = std::fs::File;
fn create_file(&mut self, name: &FileName) -> Result<std::fs::File, IoError> {
if !self.dir_created {
std::fs::create_dir_all(&self.path).map_err(|_| IoError)?;
self.dir_created = true;
}
let name = c_str_to_os_str(name.0.as_c_str()).map_err(|_| IoError)?;
let path = self.path.join(name);
std::fs::File::create(&path).map_err(|_| IoError)
}
fn create_dir(&mut self, name: &FileName) -> Result<Self, IoError> {
let name = c_str_to_os_str(name.0.as_c_str()).map_err(|_| IoError)?;
let path = self.path.join(name);
std::fs::create_dir_all(&path).map_err(|_| IoError)?;
self.dir_created = true;
Ok(Self { path, dir_created: true })
}
}
pub struct StdWriter {
path: std::path::PathBuf,
}
impl StdWriter {
pub fn new(path: std::path::PathBuf) -> Self {
Self { path }
}
pub fn path(&self) -> &std::path::Path {
&self.path
}
}
impl HostWrite for StdWriter {
type FileWrite = std::fs::File;
type DirWrite = StdDirWriter;
fn into_file_writer(self) -> Result<Self::FileWrite, IoError> {
std::fs::File::create(&self.path).map_err(|_| IoError)
}
fn into_dir_writer(self) -> Result<Self::DirWrite, IoError> {
Ok(StdDirWriter::new(self.path))
}
}
impl<R: ReadBlock> Read for NodeReader<R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
NodeReader::read(self, buf).map_err(std::io::Error::other)
}
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), std::io::Error> {
NodeReader::read_exact(self, buf).map_err(std::io::Error::other)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, std::io::Error> {
NodeReader::read_to_end(self, buf).map_err(std::io::Error::other)
}
}
impl<R: ReadBlock> Seek for NodeReader<R> {
fn seek(&mut self, relative: std::io::SeekFrom) -> Result<u64, std::io::Error> {
let absolute = match relative {
std::io::SeekFrom::Start(offset) => offset,
std::io::SeekFrom::End(offset) =>
(self.file().main_block().file_size() as i64 + offset) as u64,
std::io::SeekFrom::Current(offset) => (self.file().position() as i64 + offset) as u64,
};
NodeReader::seek(self, absolute).map_err(std::io::Error::other)?;
Ok(self.file().position())
}
}
impl<W: WriteBlock> Write for NodeWriter<W> {
fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> {
NodeWriter::write_all(self, data).map_err(std::io::Error::other)?;
Ok(data.len())
}
fn write_all(&mut self, data: &[u8]) -> Result<(), std::io::Error> {
NodeWriter::write_all(self, data).map_err(std::io::Error::other)
}
fn flush(&mut self) -> Result<(), std::io::Error> {
Ok(())
}
}