use crate::diagnostic::EnkryptitOutput;
use crate::encryption::chunk_job::{
decrypt::DecryptChunkJob, encrypt::EncryptChunkJob, submit_decrypt_chunk, submit_encrypt_chunk,
};
use crate::encryption::file::read_file;
use crate::encryption::file_encryption::multithread::receive_results;
use crate::encryption::file_encryption::multithread::write_batch;
use crate::encryption::file_encryption::multithread::write_batch_plain;
use crate::errors::EnkryptitError;
use crate::parallelism::pool::EnkryptitPool;
use crate::types::CHUNK_SIZE;
use crate::types::CompressionType;
use chacha20poly1305::{KeyInit, XChaCha20Poly1305};
use std::fs::File;
use std::io::Write;
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom};
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::Arc;
#[allow(clippy::too_many_arguments)]
pub fn encrypt_multithreading_file_into_archive(
folder_path: &str,
relative_path: &str,
file_nonce: [u8; 24],
compression: CompressionType,
cipher_key: &[u8; 32],
archive_path: &str,
pool: &EnkryptitPool<EncryptChunkJob>,
num_threads: u8,
) -> Result<u64, EnkryptitError> {
let full_file_path = Path::new(folder_path).join(relative_path);
if !PathBuf::from(&full_file_path).exists() {
tracing::warn!("Failed to encrypt an entry : path not found");
EnkryptitOutput::warning("Failed to found an entry. Skipping.").display();
return Ok(0);
}
let mut file = read_file(full_file_path)?;
let cipher = Arc::new(XChaCha20Poly1305::new(cipher_key.into()));
let mut bytes_written: u64 = 0;
let mut archive = BufWriter::new(File::options().append(true).open(archive_path)?);
let arc_compression = Arc::new(compression);
let arc_nonce = Arc::new(file_nonce);
let mut buffer = vec![0u8; CHUNK_SIZE];
let mut step: u64 = 0;
let mut results = Vec::with_capacity(num_threads as usize);
let mut submitted = 0u8;
loop {
let bytes_read = file.reader.read(&mut buffer)?;
if bytes_read == 0 {
break;
}
if submitted >= num_threads {
receive_results(&mut results, pool, num_threads)?;
bytes_written += write_batch(&mut results, &mut archive)?;
submitted = 0;
}
submit_encrypt_chunk(
pool,
step,
buffer[..bytes_read].to_vec(),
arc_nonce.clone(),
arc_compression.clone(),
cipher.clone(),
)?;
submitted += 1;
step += 1;
}
if submitted > 0 {
receive_results(&mut results, pool, submitted)?;
bytes_written += write_batch(&mut results, &mut archive)?;
}
archive.write_all(b"ENK1END")?;
bytes_written += 7;
Ok(bytes_written)
}
#[allow(clippy::too_many_arguments)]
pub fn decrypt_multithreading_file_from_archive(
archive_path: &str,
folder_path: &str,
permissions: Option<u32>,
relative_path: &str,
file_nonce: [u8; 24],
compression: CompressionType,
cipher_key: &[u8; 32],
offset: u64,
pool: &EnkryptitPool<DecryptChunkJob>,
num_threads: u8,
) -> Result<u64, EnkryptitError> {
let archive = File::open(Path::new(archive_path))?;
let mut reader = BufReader::new(archive);
reader.seek(SeekFrom::Start(offset))?;
let full_file_path = Path::new(folder_path).join(relative_path);
if let Some(parent) = full_file_path.parent() {
std::fs::create_dir_all(parent)?;
}
let file = File::create(full_file_path)?;
if let Some(p) = permissions {
file.set_permissions(std::fs::Permissions::from_mode(p))?;
}
let cipher = Arc::new(XChaCha20Poly1305::new(cipher_key.into()));
let arc_compression = Arc::new(compression);
let arc_nonce = Arc::new(file_nonce);
let mut step: u64 = 0;
let mut results = Vec::with_capacity(num_threads as usize);
let mut submitted = 0u8;
let mut bytes_written: u64 = 0;
let mut writer = BufWriter::new(file);
loop {
let mut len_buf = [0u8; 4];
match reader.read_exact(&mut len_buf) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
break;
}
Err(e) => return Err(e.into()),
}
if &len_buf == b"ENK1" {
let mut end = [0u8; 3];
reader.read_exact(&mut end)?;
if &end != b"END" {
}
break;
}
let len = u32::from_le_bytes(len_buf) as usize;
let mut payload = vec![0u8; len];
reader.read_exact(&mut payload)?;
if submitted >= num_threads {
receive_results(&mut results, pool, num_threads)?;
bytes_written += write_batch_plain(&mut results, &mut writer)?;
submitted = 0;
}
submit_decrypt_chunk(
pool,
step,
payload,
arc_nonce.clone(),
arc_compression.clone(),
cipher.clone(),
)?;
submitted += 1;
step += 1;
}
if submitted > 0 {
receive_results(&mut results, pool, submitted)?;
bytes_written += write_batch_plain(&mut results, &mut writer)?;
}
writer.flush()?;
Ok(bytes_written)
}