zff/footer/version2/
object_footer.rs

1// - STD
2use std::io::{Cursor, Read};
3use std::collections::HashMap;
4
5// - internal
6use crate::{
7	Result,
8	HeaderCoding,
9	ValueDecoder,
10	ValueEncoder,
11	ZffError,
12	ZffErrorKind,
13	FOOTER_IDENTIFIER_OBJECT_FOOTER_PHYSICAL,
14	FOOTER_IDENTIFIER_OBJECT_FOOTER_LOGICAL,
15	DEFAULT_LENGTH_VALUE_HEADER_LENGTH,
16	DEFAULT_LENGTH_HEADER_IDENTIFIER,
17	ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER,
18	ERROR_HEADER_DECODER_HEADER_LENGTH,
19};
20use crate::header::{
21	HashHeader,
22};
23
24// - external
25use byteorder::{LittleEndian, BigEndian, ReadBytesExt};
26
27/// Each object contains its own object footer.
28#[derive(Debug, Clone)]
29pub enum ObjectFooter {
30	/// A physical object contains a [ObjectFooterPhysical].
31	Physical(ObjectFooterPhysical),
32	/// A logical object contains a [ObjectFooterLogical].
33	Logical(ObjectFooterLogical),
34}
35
36impl ObjectFooter {
37	/// returns the version of the object footer.
38	pub fn version(&self) -> u8 {
39		match self {
40			ObjectFooter::Physical(phy) => phy.version(),
41			ObjectFooter::Logical(log) => log.version(),
42		}
43	}
44
45	/// checks if the identifier matches to an physical or logical object footer. Returns 1 for a physical object footer, 2 for a logical object footer and 0 if neither applies.
46	fn check_identifier<R: Read>(data: &mut R) -> u8 {
47		let identifier = match data.read_u32::<BigEndian>() {
48			Ok(val) => val,
49			Err(_) => return 0,
50		};
51		if identifier == ObjectFooterPhysical::identifier() { 
52			1
53		} else if identifier == ObjectFooterLogical::identifier() {
54			2
55		} else {
56			0
57		}
58	}
59
60	/// decodes the length of the header.
61	fn decode_header_length<R: Read>(data: &mut R) -> Result<u64> {
62		match data.read_u64::<LittleEndian>() {
63			Ok(value) => Ok(value),
64			Err(_) => Err(ZffError::new_header_decode_error(ERROR_HEADER_DECODER_HEADER_LENGTH)),
65		}
66	}
67
68	/// Reads the data from the given [Reader](std::io::Read) and returns a decoded object footer.
69	/// Returns an error, if the decoding process fails.
70	pub fn decode_directly<R: Read>(data: &mut R) -> Result<ObjectFooter> {
71		match Self::check_identifier(data) {
72			1 => {
73				let length = Self::decode_header_length(data)? as usize;
74				let mut content_buffer = vec![0u8; length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH];
75				data.read_exact(&mut content_buffer)?;
76				Ok(ObjectFooter::Physical(ObjectFooterPhysical::decode_content(content_buffer)?))
77			},
78			2 => {
79				let length = Self::decode_header_length(data)? as usize;
80				let mut content_buffer = vec![0u8; length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH];
81				data.read_exact(&mut content_buffer)?;
82				Ok(ObjectFooter::Logical(ObjectFooterLogical::decode_content(content_buffer)?))
83			},
84			_ => Err(ZffError::new(ZffErrorKind::HeaderDecodeMismatchIdentifier, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER)),
85		}
86	}
87}
88
89/// An [ObjectFooterPhysical] is written at the end of each physical object.
90/// This footer contains various information about the acquisition process:
91/// - the acquisition start time
92/// - the acquisition start time
93/// - the size of the (uncompressed and unencrypted) underlying data
94/// - the first chunk number, which is used for this physical dump
95/// - the total number of chunks, used for this physical dump
96/// - a hash header with the appropriate hash values of the underlying physical dump
97#[derive(Debug,Clone)]
98pub struct ObjectFooterPhysical {
99	version: u8,
100	acquisition_start: u64,
101	acquisition_end: u64,
102	length_of_data: u64,
103	first_chunk_number: u64,
104	number_of_chunks: u64,
105	hash_header: HashHeader,
106}
107
108impl ObjectFooterPhysical {
109	/// creates a new [ObjectFooterPhysical] with the given values.
110	pub fn new(version: u8, acquisition_start: u64, acquisition_end: u64, length_of_data: u64, first_chunk_number: u64, number_of_chunks: u64, hash_header: HashHeader) -> ObjectFooterPhysical {
111		Self {
112			version,
113			acquisition_start,
114			acquisition_end,
115			length_of_data,
116			first_chunk_number,
117			number_of_chunks,
118			hash_header,
119		}
120	}
121
122	/// returns the appropriate acquisition start time.
123	pub fn acquisition_start(&self) -> u64 {
124		self.acquisition_start
125	}
126
127	/// returns the appropriate acquisition start time.
128	pub fn acquisition_end(&self) -> u64 {
129		self.acquisition_end
130	}
131
132	/// returns the first chunk number, which is used for this physical dump.
133	pub fn first_chunk_number(&self) -> u64 {
134		self.first_chunk_number
135	}
136
137	/// returns the total number of chunks, used for this physical dump.
138	pub fn number_of_chunks(&self) -> u64 {
139		self.number_of_chunks
140	}
141
142	/// returns the size of the (uncompressed and unencrypted) underlying data.
143	pub fn length_of_data(&self) -> u64 {
144		self.length_of_data
145	}
146
147	/// returns a hash header with the appropriate hash values of the underlying physical dump.
148	pub fn hash_header(&self) -> &HashHeader {
149		&self.hash_header
150	}
151}
152
153impl HeaderCoding for ObjectFooterPhysical {
154	type Item = ObjectFooterPhysical;
155	fn version(&self) -> u8 { 
156		self.version
157	}
158	fn identifier() -> u32 {
159		FOOTER_IDENTIFIER_OBJECT_FOOTER_PHYSICAL
160	}
161	fn encode_header(&self) -> Vec<u8> {
162		let mut vec = vec![self.version];
163		vec.append(&mut self.acquisition_start.encode_directly());
164		vec.append(&mut self.acquisition_end.encode_directly());
165		vec.append(&mut self.length_of_data.encode_directly());
166		vec.append(&mut self.first_chunk_number.encode_directly());
167		vec.append(&mut self.number_of_chunks.encode_directly());
168		vec.append(&mut self.hash_header.encode_directly());
169		vec
170	}
171	fn decode_content(data: Vec<u8>) -> Result<ObjectFooterPhysical> {
172		let mut cursor = Cursor::new(data);
173		let footer_version = u8::decode_directly(&mut cursor)?;
174		let acquisition_start = u64::decode_directly(&mut cursor)?;
175		let acquisition_end = u64::decode_directly(&mut cursor)?;
176		let length_of_data = u64::decode_directly(&mut cursor)?;
177		let first_chunk_number = u64::decode_directly(&mut cursor)?;
178		let number_of_chunks = u64::decode_directly(&mut cursor)?;
179		let hash_header = HashHeader::decode_directly(&mut cursor)?;
180		Ok(ObjectFooterPhysical::new(footer_version, acquisition_start, acquisition_end, length_of_data, first_chunk_number, number_of_chunks, hash_header))
181	}
182}
183
184/// An [ObjectFooterLogical] is written at the end of each logical object container.
185/// This footer contains various information about the acquisition process:
186/// - the acquisition start time
187/// - the acquisition start time
188/// - a [Vec] of the filenumbers of the appropriate files in the root directory
189/// - a [HashMap] in which segment numbers the corresponding file headers can be found.
190/// - a [HashMap] in which offsets of the corresponding file headers can be found.
191/// - a [HashMap] in which segment numbers the corresponding file footers can be found.
192/// - a [HashMap] in which offsets the corresponding file footers can be found.
193#[derive(Debug,Clone)]
194pub struct ObjectFooterLogical {
195	version: u8,
196	acquisition_start: u64,
197	acquisition_end: u64,
198	root_dir_filenumbers: Vec<u64>,
199	file_header_segment_numbers: HashMap<u64, u64>,
200	file_header_offsets: HashMap<u64, u64>,
201	file_footer_segment_numbers: HashMap<u64, u64>,
202	file_footer_offsets: HashMap<u64, u64>,
203}
204
205impl ObjectFooterLogical {
206	/// creates a new empty [ObjectFooterLogical]
207	pub fn new_empty(version: u8) -> ObjectFooterLogical {
208		Self {
209			version,
210			acquisition_start: 0,
211			acquisition_end: 0,
212			root_dir_filenumbers: Vec::new(),
213			file_header_segment_numbers: HashMap::new(),
214			file_header_offsets: HashMap::new(),
215			file_footer_segment_numbers: HashMap::new(),
216			file_footer_offsets: HashMap::new()
217		}
218	}
219
220	/// creates a new [ObjectFooterLogical] with the given values.
221	#[allow(clippy::too_many_arguments)]
222	pub fn new(
223		version: u8,
224		acquisition_start: u64,
225		acquisition_end: u64,
226		root_dir_filenumbers: Vec<u64>,
227		file_header_segment_numbers: HashMap<u64, u64>,
228		file_header_offsets: HashMap<u64, u64>,
229		file_footer_segment_numbers: HashMap<u64, u64>,
230		file_footer_offsets: HashMap<u64, u64>) -> ObjectFooterLogical {
231		Self {
232			version,
233			acquisition_start,
234			acquisition_end,
235			root_dir_filenumbers,
236			file_header_segment_numbers,
237			file_header_offsets,
238			file_footer_segment_numbers,
239			file_footer_offsets,
240		}
241	}
242
243	/// adds a new filenumber to the underlying [Vec] of the filenumbers of the appropriate files in the root directory.
244	pub fn add_root_dir_filenumber(&mut self, filenumber: u64) {
245		self.root_dir_filenumbers.push(filenumber)
246	}
247
248	/// returns the underlying [Vec] of the filenumbers of the appropriate files in the root directory as a reference.
249	pub fn root_dir_filenumbers(&self) -> &Vec<u64> {
250		&self.root_dir_filenumbers
251	}
252
253	/// adds a file number / segment number combination to the appropriate underlying [HashMap],
254	/// which contains the appropriate segment numbers of the corresponding file headers.
255	pub fn add_file_header_segment_number(&mut self, filenumber: u64, file_segment_number: u64) {
256		self.file_header_segment_numbers.insert(filenumber, file_segment_number);
257	}
258
259	/// adds a file number / offset combination to the appropriate underlying [HashMap],
260	/// which contains the appropriate offsets of the corresponding file headers.
261	pub fn add_file_header_offset(&mut self, filenumber: u64, fileoffset: u64) {
262		self.file_header_offsets.insert(filenumber, fileoffset);
263	}
264
265	/// returns the underlying [HashMap], which contains the segment numbers and the corresponding file headers.
266	pub fn file_header_segment_numbers(&self) -> &HashMap<u64, u64> {
267		&self.file_header_segment_numbers
268	}
269
270	/// returns the underlying [HashMap], which contains the offsets and the corresponding file headers.
271	pub fn file_header_offsets(&self) -> &HashMap<u64, u64> {
272		&self.file_header_offsets
273	}
274
275	/// adds a file number / segment number combination to the appropriate underlying [HashMap],
276	/// which contains the appropriate segment numbers of the corresponding file footers.
277	pub fn add_file_footer_segment_number(&mut self, filenumber: u64, file_segment_number: u64) {
278		self.file_footer_segment_numbers.insert(filenumber, file_segment_number);
279	}
280
281	/// adds a file number / offset combination to the appropriate underlying [HashMap],
282	/// which contains the appropriate offsets of the corresponding file footers.
283	pub fn add_file_footer_offset(&mut self, filenumber: u64, fileoffset: u64) {
284		self.file_footer_offsets.insert(filenumber, fileoffset);
285	}
286
287	/// returns the underlying [HashMap], which contains the segment numbers and the corresponding file footers.
288	pub fn file_footer_segment_numbers(&self) -> &HashMap<u64, u64> {
289		&self.file_footer_segment_numbers
290	}
291
292	/// returns the underlying [HashMap], which contains the offsets and the corresponding file footers.
293	pub fn file_footer_offsets(&self) -> &HashMap<u64, u64> {
294		&self.file_footer_offsets
295	}
296
297	/// returns the acquisition start time.
298	pub fn acquisition_start(&self) -> u64 {
299		self.acquisition_start
300	}
301
302	/// returns the acquisition end time.
303	pub fn acquisition_end(&self) -> u64 {
304		self.acquisition_end
305	}
306
307	/// sets the acquisition start time.
308	pub fn set_acquisition_start(&mut self, start: u64) {
309		self.acquisition_start = start;
310	}
311
312	/// sets the acquisition end time.
313	pub fn set_acquisition_end(&mut self, end: u64) {
314		self.acquisition_end = end;
315	}
316}
317
318impl HeaderCoding for ObjectFooterLogical {
319	type Item = ObjectFooterLogical;
320
321	fn version(&self) -> u8 { 
322		self.version
323	}
324	fn identifier() -> u32 {
325		FOOTER_IDENTIFIER_OBJECT_FOOTER_LOGICAL
326	}
327	fn encode_header(&self) -> Vec<u8> {
328		let mut vec = vec![self.version];
329		vec.append(&mut self.acquisition_start.encode_directly());
330		vec.append(&mut self.acquisition_end.encode_directly());
331		vec.append(&mut self.root_dir_filenumbers.encode_directly());
332		vec.append(&mut self.file_header_segment_numbers.encode_directly());
333		vec.append(&mut self.file_header_offsets.encode_directly());
334		vec.append(&mut self.file_footer_segment_numbers.encode_directly());
335		vec.append(&mut self.file_footer_offsets.encode_directly());
336		vec
337	}
338	fn decode_content(data: Vec<u8>) -> Result<ObjectFooterLogical> {
339		let mut cursor = Cursor::new(data);
340		let footer_version = u8::decode_directly(&mut cursor)?;
341		let acquisition_start = u64::decode_directly(&mut cursor)?;
342		let acquisition_end = u64::decode_directly(&mut cursor)?;
343		let root_dir_filenumbers = Vec::<u64>::decode_directly(&mut cursor)?;
344		let file_header_segment_numbers = HashMap::<u64, u64>::decode_directly(&mut cursor)?;
345		let file_header_offsets = HashMap::<u64, u64>::decode_directly(&mut cursor)?;
346		let file_footer_segment_numbers = HashMap::<u64, u64>::decode_directly(&mut cursor)?;
347		let file_footer_offsets = HashMap::<u64, u64>::decode_directly(&mut cursor)?;
348		Ok(ObjectFooterLogical::new(footer_version, acquisition_start, acquisition_end, root_dir_filenumbers, file_header_segment_numbers, file_header_offsets, file_footer_segment_numbers, file_footer_offsets))
349	}
350
351}