1use std::cell::RefCell;
3use std::io::{Cursor, Read};
4use std::path::PathBuf;
5use std::rc::Rc;
6use std::time::SystemTime;
7
8#[cfg(feature = "log")]
10use crate::hashes_to_log;
11use crate::prelude::*;
12use crate::{
13 EncodingState, EncodingThreadPoolManager, PreparedChunk, Signature, chunking,
14 io::{BufferedChunk, buffer_chunk},
15};
16
17use ed25519_dalek::SigningKey;
19use time::OffsetDateTime;
20use zeroize::Zeroize;
21
22pub enum FileTypeEncodingInformation {
24 File(Box<dyn Read>),
26 Directory(Vec<u64>), Symlink(PathBuf), Hardlink(u64), SpecialFile(SpecialFileEncodingInformation), }
35
36#[derive(Debug, Clone, PartialEq, Eq, Copy)]
38pub enum SpecialFileEncodingInformation {
39 Fifo(u64), Char(u64), Block(u64), Socket(u64), }
48
49pub struct FileEncoder {
54 file_header: FileHeader,
56 object_header: ObjectHeader,
58 filetype_encoding_information: FileTypeEncodingInformation,
59 signing_key: Option<SigningKey>,
61 encryption_information: Option<EncryptionInformation>,
63 encoding_thread_pool_manager: Rc<RefCell<EncodingThreadPoolManager>>,
65 initial_chunk_number: u64,
67 current_chunk_number: u64,
69 read_bytes_underlying_data: u64,
71 acquisition_start: u64,
72 acquisition_end: u64,
73}
74
75impl Drop for FileEncoder {
76 fn drop(&mut self) {
77 self.encryption_information.zeroize();
78 }
79}
80
81impl FileEncoder {
82 pub fn new(
84 file_header: FileHeader,
85 object_header: ObjectHeader,
86 filetype_encoding_information: FileTypeEncodingInformation,
87 encoding_thread_pool_manager: Rc<RefCell<EncodingThreadPoolManager>>,
88 signing_key: Option<SigningKey>,
89 encryption_information: Option<EncryptionInformation>,
90 current_chunk_number: u64,
91 ) -> Result<FileEncoder> {
92 Ok(Self {
93 file_header,
94 object_header,
95 encoding_thread_pool_manager,
96 signing_key,
97 encryption_information,
98 initial_chunk_number: current_chunk_number,
99 current_chunk_number,
100 read_bytes_underlying_data: 0,
101 acquisition_start: 0,
102 acquisition_end: 0,
103 filetype_encoding_information,
104 })
105 }
106
107 pub fn file_header_ref(&self) -> &FileHeader {
109 &self.file_header
110 }
111
112 pub(crate) fn set_initial_chunk_number(&mut self, chunk_number: u64) {
113 self.initial_chunk_number = chunk_number;
114 self.current_chunk_number = chunk_number;
115 }
116
117 pub fn get_encoded_header(&mut self) -> Result<Vec<u8>> {
121 if self.acquisition_start == 0 {
122 self.acquisition_start =
123 OffsetDateTime::from(SystemTime::now()).unix_timestamp() as u64;
124 }
125 if let Some(enc_info) = &self.encryption_information {
126 self.file_header.encrypt_directly(enc_info)
127 } else {
128 Ok(self.file_header.encode_directly())
129 }
130 }
131
132 pub(crate) fn get_next_chunk<D: ReadAt>(
134 &mut self,
135 deduplication_metadata: Option<&mut DeduplicationMetadata<D>>,
136 ) -> Result<EncodingState> {
137 let chunk_size = self.object_header.chunk_size as usize;
138 let mut eof = false;
139
140 let buffered_chunk = match &mut self.filetype_encoding_information {
141 FileTypeEncodingInformation::Directory(directory_children) => {
142 let encoded_directory_children = if directory_children.is_empty() {
143 Vec::<u64>::new().encode_directly()
144 } else {
145 directory_children.encode_directly()
146 };
147 let mut cursor = Cursor::new(&encoded_directory_children);
148 cursor.set_position(self.read_bytes_underlying_data);
149 let buffered_chunk = buffer_chunk(&mut cursor, chunk_size)?;
150 self.read_bytes_underlying_data += buffered_chunk.bytes_read;
151 buffered_chunk
152 }
153 FileTypeEncodingInformation::Symlink(symlink_real_path) => {
154 let encoded_symlink_real_path =
155 PlatformString::from(symlink_real_path.as_os_str()).encode_directly();
156 let mut cursor = Cursor::new(&encoded_symlink_real_path);
157 cursor.set_position(self.read_bytes_underlying_data);
158 let buffered_chunk = buffer_chunk(&mut cursor, chunk_size)?;
159 self.read_bytes_underlying_data += buffered_chunk.bytes_read;
160 if buffered_chunk.bytes_read > 0 {
161 buffered_chunk
162 } else {
163 eof = true;
164 BufferedChunk::default()
165 }
166 }
167 FileTypeEncodingInformation::Hardlink(hardlink_filenumber) => {
168 let encoded_hardlink_filenumber = hardlink_filenumber.encode_directly();
169 let mut cursor = Cursor::new(&encoded_hardlink_filenumber);
170 cursor.set_position(self.read_bytes_underlying_data);
171 let buffered_chunk = buffer_chunk(&mut cursor, chunk_size)?;
172 self.read_bytes_underlying_data += buffered_chunk.bytes_read;
173 if buffered_chunk.bytes_read > 0 {
174 buffered_chunk
175 } else {
176 eof = true;
177 BufferedChunk::default()
178 }
179 }
180 FileTypeEncodingInformation::File(reader) => {
181 let buffered_chunk = buffer_chunk(reader, chunk_size)?;
182 self.read_bytes_underlying_data += buffered_chunk.bytes_read;
183 buffered_chunk
184 }
185 FileTypeEncodingInformation::SpecialFile(specialfile_encoding_information) => {
188 let (rdev_id, type_flag) = match specialfile_encoding_information {
189 SpecialFileEncodingInformation::Fifo(rdev_id) => (rdev_id, 0_u8),
190 SpecialFileEncodingInformation::Char(rdev_id) => (rdev_id, 1),
191 SpecialFileEncodingInformation::Block(rdev_id) => (rdev_id, 2),
192 SpecialFileEncodingInformation::Socket(rdev_id) => (rdev_id, 3),
193 };
194 let mut encoded_data = rdev_id.encode_directly();
195 encoded_data.append(&mut type_flag.encode_directly());
196 let mut cursor = Cursor::new(&encoded_data);
197 cursor.set_position(self.read_bytes_underlying_data);
198 let buffered_chunk = buffer_chunk(&mut cursor, chunk_size)?;
199 self.read_bytes_underlying_data += buffered_chunk.bytes_read;
200 if buffered_chunk.bytes_read > 0 {
201 buffered_chunk
202 } else {
203 eof = true;
204 BufferedChunk::default()
205 }
206 }
207 };
208
209 if buffered_chunk.buffer.is_empty() && self.read_bytes_underlying_data != 0 || eof {
210 return Ok(EncodingState::ReadEOF);
212 } else if buffered_chunk.buffer.is_empty() && self.read_bytes_underlying_data == 0 {
213 let chunk_header = ChunkHeader {
215 flags: ChunkFlags {
216 empty_file: true,
217 ..Default::default()
218 },
219 ..Default::default()
220 };
221 let prepared_chunk = PreparedChunk::new(Vec::new(), chunk_header, None, None);
222 return Ok(EncodingState::PreparedChunk(prepared_chunk));
223 };
224
225 let buf_len = buffered_chunk.buffer.len() as u64;
227
228 let mut encoding_thread_pool_manager = self.encoding_thread_pool_manager.borrow_mut();
229
230 encoding_thread_pool_manager.update(buffered_chunk.buffer)?;
231
232 let encryption_algorithm = self
233 .encryption_information
234 .as_ref()
235 .map(|encryption_information| &encryption_information.algorithm);
236 let encryption_key = self
237 .encryption_information
238 .as_ref()
239 .map(|encryption_information| &encryption_information.encryption_key);
240
241 let chunk = chunking(
242 &mut encoding_thread_pool_manager,
243 self.current_chunk_number,
244 buf_len,
245 chunk_size as u64,
246 deduplication_metadata,
247 encryption_key,
248 encryption_algorithm,
249 )?;
250
251 self.current_chunk_number += 1;
252 Ok(EncodingState::PreparedChunk(chunk))
253 }
254
255 pub fn get_encoded_footer(&mut self) -> Result<Vec<u8>> {
258 let mut encoding_thread_pool_manager = self.encoding_thread_pool_manager.borrow_mut();
259
260 self.acquisition_end = OffsetDateTime::from(SystemTime::now()).unix_timestamp() as u64;
261 let mut hash_values = Vec::new();
262 for (hash_type, hash) in encoding_thread_pool_manager.finalize_all_hashing_threads()? {
263 let mut hash_value = HashValue::new_empty(hash_type.clone());
264 hash_value.set_hash(hash.to_vec());
265 if let Some(signing_key) = &self.signing_key {
266 let signature = Signature::sign(signing_key, &hash);
267 hash_value.set_ed25519_signature(signature);
268 }
269 hash_values.push(hash_value);
270 }
271
272 #[cfg(feature = "log")]
273 hashes_to_log(
274 self.object_header.object_number,
275 Some(self.file_header.file_number),
276 &hash_values,
277 );
278
279 let hash_header = HashHeader::new(hash_values);
280 let footer = FileFooter::new(
281 self.file_header.file_number,
282 self.acquisition_start,
283 self.acquisition_end,
284 hash_header,
285 self.initial_chunk_number,
286 self.current_chunk_number - self.initial_chunk_number,
287 self.read_bytes_underlying_data,
288 );
289 if let Some(enc_info) = &self.encryption_information {
290 footer.encrypt_directly(enc_info)
291 } else {
292 Ok(footer.encode_directly())
293 }
294 }
295}