Started feature `mmap`: Attempt mapping of stdio streams instead of buffered processing.

Fortune for chacha20's current commit: Small blessing − 小吉
mmap-feature
Avril 2 years ago
parent 38dcccd001
commit e707df9a12
Signed by: flanchan
GPG Key ID: 284488987C31F630

5
Cargo.lock generated

@ -38,6 +38,7 @@ version = "2.0.0"
dependencies = [
"base64",
"getrandom",
"libc",
"openssl",
"rustc_version",
"smallvec",
@ -71,9 +72,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.90"
version = "0.2.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae"
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
[[package]]
name = "once_cell"

@ -3,17 +3,21 @@ name = "chacha20"
description = "chacha20_poly1305 encryption tool"
version = "2.0.0"
authors = ["Avril <flanchan@cumallover.me>"]
edition = "2018"
edition = "2021"
license = "gpl-3.0-or-later"
[features]
default = ["mmap"]
# Try to map inputs/outputs before using buffers
mmap = ["libc"]
# Explicitly clear buffers and cache after use
explicit_clear = []
[dependencies]
base64 = "0.13"
getrandom = "0.2"
libc = { version = "0.2.133", optional = true }
openssl = "0.10"
smallvec = {version = "1.6", features=["union"]}

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

@ -0,0 +1,110 @@
//! Use memory mapping to process the entire stream at once
use super::*;
use std::os::unix::prelude::*;
use std::{
io,
fs,
ptr,
ops,
mem::{
self,
MaybeUninit,
},
borrow::BorrowMut,
convert::{TryFrom, TryInto,},
};
use libc::{
mmap, munmap, madvise, MAP_FAILED,
};
use openssl::symm::Crypter;
#[derive(Debug)]
struct MapInner
{
mem: ptr::NonNull<u8>,
size: usize
}
impl ops::Drop for MapInner
{
#[inline]
fn drop(&mut self)
{
unsafe {
munmap(self.mem.as_ptr() as *mut _, self.size);
}
}
}
#[inline]
pub fn raw_file_size(fd: &(impl AsRawFd + ?Sized)) -> io::Result<u64>
{
use libc::fstat;
let mut stat = MaybeUninit::uninit();
match unsafe { fstat(fd.as_raw_fd(), stat.as_mut_ptr()) } {
0 => match unsafe {stat.assume_init()}.st_size {
x if x < 0 => Err(io::Error::new(io::ErrorKind::InvalidInput, format!("File from {} is too large", fd.as_raw_fd()))),
x => Ok(x as u64),
},
_ => Err(io::Error::last_os_error()),
}
}
#[derive(Debug)]
pub struct Mapped<T: ?Sized>
{
mem: MapInner,
file: T
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
pub enum MapMode
{
ReadOnly,
ReadWrite,
WriteOnly,
}
//TODO: impl MapMode -> fn as_prot() -> c_int, fn as_flags() -> c_int
impl<T: AsRawFd> Mapped<T>
{
pub fn try_new_sized(file: T, size: u64, rw: MapMode) -> io::Result<Self>
{
todo!("mem: mmap(0, size, rw.prot(), MAP_SHARED (For rw: *Write*), MAP_PRIVATE [TODO: add support for| MAP_HUGE_] (For rw: ReadOnly), file.as_raw_fd(), 0) != MAP_FAILED (TODO: maybe madvise?)")
}
#[inline(always)]
pub fn try_new(file: T, rw: MapMode) -> io::Result<Self>
{
let size = raw_file_size(&file)?;
Self::try_new_sized(file, size, rw)
}
}
/*
impl<T: AsRawFd> TryFrom<T> for Mapped<T>
{
type Error = io::Error;
fn try_from(from: T) -> Result<Self, Self::Error>
{
Self::try_new(from, raw_file_size(&from)?)
}
}*/
fn process_mapped_files<T, U>(mode: &mut Crypter, input: &mut Mapped<T>, output: &mut Mapped<U>) -> io::Result<()>
where T: AsRawFd + ?Sized,
U: AsRawFd + ?Sized
{
todo!("mode.update(input.slice(), output.slice()); /* unneeded: mode.finalize(input.slice(), output.slice()) */ ");
}
#[inline]
pub fn try_process(mut mode: impl BorrowMut<Crypter>) -> io::Result<io::Result<()>>
{
let mode = mode.borrow_mut();
/*let mstdin = Mapped::try_new(input)?;
let mstdout = Mapped::try_new(output)?;*/ //TODO: if failed to map output, but input is successful (TODO: XXX: implement other way around), we can fall back to wrapping output in `Sink`.
todo!("Try to map the stdin and stdout streams, if that fails, return Err(last_os_err).");
todo!("return Ok(process_mapped_files(mode, mstdin, mstdout, key, iv))")
}
Loading…
Cancel
Save