parent
fd2ad0fdb4
commit
8ee5617d13
@ -0,0 +1,101 @@
|
||||
use super::*;
|
||||
use std::{
|
||||
fs,io,
|
||||
path::Path,
|
||||
fmt, error,
|
||||
};
|
||||
use fs::File;
|
||||
use std::borrow::Cow;
|
||||
use std::os::unix::io::AsRawFd as _;
|
||||
|
||||
fn fallocate(file: &mut File, to: usize) -> Result<(), OutputError>
|
||||
{
|
||||
let fd = file.as_raw_fd();
|
||||
unsafe {
|
||||
if libc::fallocate(fd, 0, 0, to as libc::off_t) == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
let errno = *libc::__errno_location();
|
||||
Err(OutputError::Allocate(errno))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reserved output file.
|
||||
#[derive(Debug)]
|
||||
pub struct Output(File);
|
||||
|
||||
impl Output
|
||||
{
|
||||
/// Allocate this reserved output file `to` bytes, the map it and return.
|
||||
pub fn complete(self, to: usize) -> Result<map::MemoryMapMut, OutputError>
|
||||
{
|
||||
let mut file = self.0;
|
||||
fallocate(&mut file, to)?;
|
||||
map::MemoryMapMut::map(file)
|
||||
.map_err(OutputError::Map)
|
||||
.and_then(|map| if map.len() != to {
|
||||
Err(OutputError::Size{expected: to, got: map.len()})
|
||||
} else {
|
||||
Ok(map)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a reserved output fd
|
||||
#[inline] pub fn output(path: impl AsRef<Path>) -> Result<Output, OutputError>
|
||||
{
|
||||
fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.map(Output)
|
||||
.map_err(OutputError::Open)
|
||||
}
|
||||
|
||||
fn errno_str<'a>(errno: libc::c_int) -> Cow<'a, str>
|
||||
{
|
||||
unsafe {
|
||||
let ptr = libc::strerror(errno) as *const libc::c_char;
|
||||
if ptr.is_null() {
|
||||
return Cow::Borrowed("Unknown");
|
||||
}
|
||||
let cstr = std::ffi::CStr::from_ptr(ptr);
|
||||
cstr.to_string_lossy()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum OutputError {
|
||||
Open(io::Error),
|
||||
Allocate(libc::c_int),
|
||||
Map(io::Error),
|
||||
Size{expected: usize, got: usize},
|
||||
}
|
||||
impl error::Error for OutputError
|
||||
{
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)>
|
||||
{
|
||||
match &self {
|
||||
Self::Open(io)
|
||||
| Self::Map(io)
|
||||
=> Some(io),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for OutputError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
match self {
|
||||
Self::Open(io) => write!(f, "Failed to open file: {}", io),
|
||||
Self::Allocate(errno) => write!(f, "fallocate() failed with error {}: {}", errno, errno_str(*errno)),
|
||||
Self::Map(io) => write!(f, "mmap() failed: {}", io),
|
||||
Self::Size{expected, got} => write!(f, "mapped file size mismatch: expected {}, got {}", expected, got),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in new issue