Tested mapping modes "Input" and "Both" work. (And I think "neither" does too?)

Added feature `unsafe-mappings` which allows forceful mappings of output files; this is dangerous however as it cannot respect `>>` vs. `>`, so it"s its own feature.

Fortune for chacha20's current commit: Future curse − 末凶
mmap-feature
Avril 2 years ago
parent 72a55f897a
commit 03b33e6472
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -11,6 +11,10 @@ default = ["mmap"]
# Try to map inputs/outputs before using buffers # Try to map inputs/outputs before using buffers
mmap = ["libc"] mmap = ["libc"]
# Forcefully map all output real files.
# This is unsafe because we cannot distinguish the offset at which to map the file descriptor, or if there even is one.
unsafe-mappings = ["mmap"]
# Explicitly clear buffers and cache after use # Explicitly clear buffers and cache after use
explicit_clear = [] explicit_clear = []

@ -93,34 +93,35 @@ const USE_MMAP: bool = if cfg!(feature="mmap") {
mod mapped; mod mapped;
#[allow(unreachable_code)] #[allow(unreachable_code)]
fn try_mmap(decrypt: bool, key: Key, iv: IV) -> std::io::Result<i32> fn try_mmap(decrypt: bool, key: Key, iv: IV) -> Result<i32, mapped::ProcessError>
{ {
#[cfg(feature="mmap")] return mapped::try_process(if decrypt { #[cfg(feature="mmap")] return mapped::try_process(if decrypt {
cha::decrypter(key, iv).expect("Failed to create decrypter") cha::decrypter(key, iv).expect("Failed to create decrypter")
} else { } else {
cha::encrypter(key, iv).expect("Failed to create encrypter") cha::encrypter(key, iv).expect("Failed to create encrypter")
}).map(|_| 0); }).map(|_| 0i32);
unreachable!("Built without feature `mmap`, but still tried to call into it. This is a bug") unreachable!("Built without feature `mmap`, but still tried to call into it. This is a bug")
} }
fn main() { fn main() {
let (mode, key, iv) = keys().expect("Failed to read keys from argv (base64)"); let (mode, key, iv) = keys().expect("Failed to read keys from argv (base64)");
let stdout = std::io::stdout();
let input = std::io::stdin();
// Attempt a mapped solution // Attempt a mapped solution
if USE_MMAP && mode != Mode::Keygen { if USE_MMAP && mode != Mode::Keygen {
match try_mmap(mode == Mode::Decrypt, key, iv) { match try_mmap(mode == Mode::Decrypt, key, iv) {
Ok(0) => return, Ok(0) => return,
Ok(n) => std::process::exit(n), Ok(n) => std::process::exit(n),
Err(err) => if cfg!(debug_assertions) { Err(err) => if cfg!(debug_assertions) {
eprintln!("Failed to mmap input or output for processing, falling back to stream: {}", err); eprintln!("Failed to mmap input or output for processing, falling back to stream: {}", &err);
eprintln!("\t{:?}", err);
} }
} }
} }
let stdout = std::io::stdout();
let input = std::io::stdin();
// Streaming // Streaming
use std::io::Write; use std::io::Write;
match mode match mode

@ -169,10 +169,12 @@ fn translate_hugetlb_size(size: usize) -> mapped_file::hugetlb::HugePage
} }
const MB: usize = 1024*1024; const MB: usize = 1024*1024;
const GB: usize = 1024 * MB; const GB: usize = 1024 * MB;
#[allow(overlapping_range_endpoints)]
match size { match size {
0..=MB => mapped_file::hugetlb::HugePage::Smallest, 0..=MB => mapped_file::hugetlb::HugePage::Smallest,
MB..=GB => mapped_file::hugetlb::HugePage::Selected(check_func), MB..=GB => mapped_file::hugetlb::HugePage::Selected(check_func),
very_high => mapped_file::hugetlb::HugePage::Largest, _very_high => mapped_file::hugetlb::HugePage::Largest,
} }
} }
@ -195,7 +197,7 @@ fn create_sized_temp_mapping_at<const HUGE: bool>(size: usize) -> io::Result<(Ma
{ {
const HUGETLB_THRESH: usize = 1024 * 1024; // 1MB const HUGETLB_THRESH: usize = 1024 * 1024; // 1MB
let file = match size { let file = match size {
0..=HUGETLB_THRESH => MemoryFile::with_size(size), 1..=HUGETLB_THRESH => MemoryFile::with_size(size),
size if HUGE => { size if HUGE => {
let hugetlb = translate_hugetlb_size(size); let hugetlb = translate_hugetlb_size(size);
let file = if let Some(flag) =hugetlb.compute_huge() { let file = if let Some(flag) =hugetlb.compute_huge() {
@ -221,6 +223,7 @@ type MappedMemoryFile = MappedFile<mapped_file::file::memory::MemoryFile>;
/// How a mapped operation should optimally take place /// How a mapped operation should optimally take place
//TODO: Make optimised mapping operations for each one, like `fn process(self, enc: &mut Crypter) -> io::Result<usize>` //TODO: Make optimised mapping operations for each one, like `fn process(self, enc: &mut Crypter) -> io::Result<usize>`
#[derive(Debug)]
pub enum OpTable<T, U> pub enum OpTable<T, U>
{ {
/// Both input and output are mapped /// Both input and output are mapped
@ -387,6 +390,19 @@ fn map_size_or<T: AsRawFd, U, F>(stream: T, size: usize, trans: F) -> Result<U,
{ {
if try_map_to(&stream, size) { if try_map_to(&stream, size) {
// Sized // Sized
if cfg!(feature="unsafe-mappings") {
// Ensure the output file is open for writing
// XXX: This is not safe, we can't tell if it's a new file being created or if it's appending. I dunno how to find out, lseek doesn't say.
if let Err(err) = reopen_rw(&stream) {
// If this fails, we cannot map it.
if cfg!(debug_assertions) {
eprintln!("Warning: Failed to re-open stdout: {}", err);
}
return Err(stream);
}
}
// Then map read+write
match MappedFile::try_new(stream, size, Perm::ReadWrite, Flags::Shared) { match MappedFile::try_new(stream, size, Perm::ReadWrite, Flags::Shared) {
Ok(map) => Ok(trans(map, size)), Ok(map) => Ok(trans(map, size)),
Err(e) => Err(e.into_inner()), Err(e) => Err(e.into_inner()),
@ -432,7 +448,16 @@ impl fmt::Debug for ProcessError
.finish_non_exhaustive() .finish_non_exhaustive()
} }
} }
impl error::Error for ProcessError{} impl error::Error for ProcessError
{
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(match self.kind {
ProcessErrorKind::IO(ref io) => io,
_ => return None
})
}
}
impl fmt::Display for ProcessError impl fmt::Display for ProcessError
{ {
#[inline] #[inline]
@ -456,10 +481,58 @@ impl From<io::Error> for ProcessError
} }
} }
impl ProcessError
{
#[inline]
pub fn context_mut(&mut self) -> Option<&mut Dynamic>
{
self.context.as_deref_mut()
}
#[inline]
pub fn into_context(self) -> Option<Box<Dynamic>>
{
self.context
}
#[inline]
pub fn context(&self) -> Option<&Dynamic>
{
self.context.as_deref()
}
}
/// Reopen a file-descriptor as read+write.
pub fn reopen_rw(fd: &(impl AsRawFd+?Sized)) -> io::Result<()>
{
let fd = fd.as_raw_fd();
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.open(format!("/proc/self/fd/{fd}"))?;
// Attempt to seek new fd to old's position, this is important
if unsafe {
let size = libc::lseek(fd, 0, libc::SEEK_CUR);
libc::lseek(file.as_raw_fd(), match size {
-1 => return Err(io::Error::last_os_error()),
v => v,
}, libc::SEEK_SET)
} < 0 {
return Err(io::Error::last_os_error());
}
// File descriptor set up to track accurately
unsafe {
let res = libc::dup2(file.as_raw_fd(), fd);
if res < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
/// Create an optimised call table for the cryptographic transformation from `from` to `to`. /// Create an optimised call table for the cryptographic transformation from `from` to `to`.
pub fn try_create_process<T: AsRawFd, U: AsRawFd>(from: T, to: U) -> Result<OpTable<T, U>, ProcessError> pub fn try_create_process<T: AsRawFd + io::Read, U: AsRawFd + io::Write>(from: T, to: U) -> Result<OpTable<T, U>, ProcessError>
{ {
let (input, buffsz) = match sized_then_or(from, |input, input_size| { let (input, buffsz) = match sized_then_or(from, |input, input_size| {
(match MappedFile::try_new(input, input_size, Perm::Readonly, Flags::Private) { (match MappedFile::try_new(input, input_size, Perm::Readonly, Flags::Private) {
@ -493,9 +566,17 @@ pub fn try_create_process<T: AsRawFd, U: AsRawFd>(from: T, to: U) -> Result<OpTa
}) })
} }
pub fn try_process(_mode: impl BorrowMut<Crypter>) -> io::Result<io::Result<()>> #[inline]
//TODO: Add metrics, status, progress, diagnostics, etc. reporting
pub fn try_process(mode: impl BorrowMut<Crypter>) -> Result<usize, ProcessError>
{ {
todo!("use `try_create_process()`") //XXX: <- next thing to do is integrate the above function into the caller for this (the old impl) one let sin = io::stdin().lock();
let sout = io::stdout().lock();
let proc = try_create_process(sin, sout)?;
if cfg!(debug_assertions) {
eprintln!("Process is: {:?}", proc);
}
Ok(proc.execute(mode)?)
} }
#[cfg(feature="try_process-old")] #[cfg(feature="try_process-old")]

Loading…
Cancel
Save