zff/header/version2/
file_header.rs

1// - STD
2use std::io::{Cursor, Read};
3use std::collections::HashMap;
4use std::fmt;
5
6// - internal
7use crate::{
8	Result,
9	HeaderCoding,
10	ValueDecoder,
11	ValueEncoder,
12	Encryption,
13	ZffError,
14	ZffErrorKind,
15};
16
17use crate::{
18	DEFAULT_LENGTH_HEADER_IDENTIFIER,
19	DEFAULT_LENGTH_VALUE_HEADER_LENGTH,
20	HEADER_IDENTIFIER_FILE_HEADER,
21	ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER
22};
23
24use crate::header::{
25	EncryptionHeader,
26};
27
28/// Defines all file types, which are implemented for zff files.
29#[repr(u8)]
30#[non_exhaustive]
31#[derive(Debug,Clone,Eq,PartialEq,Hash)]
32pub enum FileType {
33	/// Represents a regular file (e.g. like "textfile.txt").
34	File = 1,
35	/// Represents a directory.
36	Directory = 2,
37	/// Represents a symbolic link.
38	Symlink = 3,
39	/// Represents a hard link (mostly used at unix like operating systems).
40	Hardlink = 4,
41}
42
43impl fmt::Display for FileType {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45		let msg = match self {
46			FileType::File => "File",
47			FileType::Directory => "Directory",
48			FileType::Symlink => "Symlink",
49			FileType::Hardlink => "Hardlink",
50		};
51		write!(f, "{}", msg)
52	}
53}
54
55/// Each dumped file* contains a [FileHeader] containing several metadata.
56/// The following metadata are included in a [FileHeader]:
57/// - the internal file number of the appropriate file.
58/// - the [FileType] of the appropriate file.
59/// - the original filename of the appropriate file **without** the full path (just the filename, e.g. "my_texfile.txt" or "my_directory")
60/// - the file number of the parent directory of this file (if the file lies into the root directory, this is 0 because the first valid file number in zff is 1).
61/// - the atime, mtime, ctime and btime.
62/// - A HashMap to extend the metadata based on the operating system/filesystem. Some fields are predefined, see [the full list in the wiki](https://github.com/ph0llux/zff/wiki/zff-header-layout#file-metadata-extended-information)
63#[derive(Debug,Clone,Eq,PartialEq)]
64pub struct FileHeader {
65	version: u8,
66	file_number: u64,
67	file_type: FileType,
68	filename: String,
69	parent_file_number: u64,
70	atime: u64,
71	mtime: u64,
72	ctime: u64,
73	btime: u64,
74	metadata_ext: HashMap<String, String>,
75}
76
77impl FileHeader {
78	/// creates a new [FileHeader] with the given values.
79	#[allow(clippy::too_many_arguments)]
80	pub fn new<F: Into<String>>(
81		version: u8,
82		file_number: u64,
83		file_type: FileType,
84		filename: F,
85		parent_file_number: u64,
86		atime: u64,
87		mtime: u64,
88		ctime: u64,
89		btime: u64,
90		metadata_ext: HashMap<String, String>) -> FileHeader {
91		Self {
92			version,
93			file_number,
94			file_type,
95			filename: filename.into(),
96			parent_file_number,
97			atime,
98			mtime,
99			ctime,
100			btime,
101			metadata_ext
102		}
103	}
104	/// returns the file number
105	pub fn file_number(&self) -> u64 {
106		self.file_number
107	}
108	/// returns the [FileType]
109	pub fn file_type(&self) -> FileType {
110		self.file_type.clone()
111	}
112	/// returns the filename
113	pub fn filename(&self) -> &str {
114		&self.filename
115	}
116	/// returns the file number of the parent directory
117	pub fn parent_file_number(&self) -> u64 {
118		self.parent_file_number
119	}
120	/// returns the atime
121	pub fn atime(&self) -> u64 {
122		self.atime
123	}
124	/// returns the mtime 
125	pub fn mtime(&self) -> u64 {
126		self.mtime
127	}
128	/// returns the ctime
129	pub fn ctime(&self) -> u64 {
130		self.ctime
131	}
132	/// returns the btime
133	pub fn btime(&self) -> u64 {
134		self.btime
135	}
136	/// returns the extended metadata [HashMap] as a reference.
137	pub fn metadata_ext(&self) -> &HashMap<String, String> {
138		&self.metadata_ext
139	}
140
141	/// transforms the inner [FileType] to a [FileType::Hardlink]. This does not work with a [FileType::Symlink]!
142	pub fn transform_to_hardlink(&mut self) {
143		if self.file_type != FileType::Symlink {
144			self.file_type = FileType::Hardlink
145		}
146	}
147
148	/// encodes the file header to a ```Vec<u8>```. The encryption flag of the appropriate object header has to be set to 2.
149	/// # Error
150	/// The method returns an error, if the encryption fails.
151	pub fn encode_encrypted_header_directly<K>(&self, key: K, encryption_header: EncryptionHeader) -> Result<Vec<u8>>
152	where
153		K: AsRef<[u8]>,
154	{
155		let mut vec = Vec::new();
156		let mut encoded_header = self.encode_encrypted_header(key, encryption_header)?;
157		let identifier = HEADER_IDENTIFIER_FILE_HEADER;
158		let encoded_header_length = 4 + 8 + (encoded_header.len() as u64); //4 bytes identifier + 8 bytes for length + length itself
159		vec.append(&mut identifier.to_be_bytes().to_vec());
160		vec.append(&mut encoded_header_length.to_le_bytes().to_vec());
161		vec.append(&mut encoded_header);
162
163		Ok(vec)
164	}
165
166	fn encode_encrypted_header<K>(&self, key: K, encryption_header: EncryptionHeader) -> Result<Vec<u8>>
167	where
168		K: AsRef<[u8]>
169	{
170		let mut vec = Vec::new();
171		vec.append(&mut self.version.encode_directly());
172		vec.append(&mut self.file_number.encode_directly());
173
174		let mut data_to_encrypt = Vec::new();
175		data_to_encrypt.append(&mut self.encode_content());
176
177		let encrypted_data = Encryption::encrypt_header(
178			key, data_to_encrypt,
179			encryption_header.nonce(),
180			encryption_header.algorithm()
181			)?;
182		vec.append(&mut encrypted_data.encode_directly());
183		Ok(vec)
184	}
185
186	fn encode_content(&self) -> Vec<u8> {
187		let mut vec = Vec::new();
188		vec.append(&mut (self.file_type.clone() as u8).encode_directly());
189		vec.append(&mut self.filename().encode_directly());
190		vec.append(&mut self.parent_file_number.encode_directly());
191		vec.append(&mut self.atime.encode_directly());
192		vec.append(&mut self.mtime.encode_directly());
193		vec.append(&mut self.ctime.encode_directly());
194		vec.append(&mut self.btime.encode_directly());
195		vec.append(&mut self.metadata_ext.encode_directly());
196		vec
197	}
198
199	/// decodes the encrypted header with the given key and [crate::header::EncryptionHeader].
200	/// The appropriate [crate::header::EncryptionHeader] has to be stored in the appropriate [crate::header::ObjectHeader].
201	pub fn decode_encrypted_header_with_key<R, K>(data: &mut R, key: K, encryption_header: EncryptionHeader) -> Result<FileHeader>
202	where
203		R: Read,
204		K: AsRef<[u8]>,
205	{
206		if !Self::check_identifier(data) {
207			return Err(ZffError::new(ZffErrorKind::HeaderDecodeMismatchIdentifier, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER));
208		};
209		let header_length = Self::decode_header_length(data)? as usize;
210		let mut header_content = vec![0u8; header_length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH];
211		data.read_exact(&mut header_content)?;
212		let mut cursor = Cursor::new(header_content);
213		let header_version = u8::decode_directly(&mut cursor)?;
214		let file_number = u64::decode_directly(&mut cursor)?;
215		
216		let encrypted_data = Vec::<u8>::decode_directly(&mut cursor)?;
217		let nonce = encryption_header.nonce();
218		let algorithm = encryption_header.algorithm();
219		let decrypted_data = Encryption::decrypt_header(key, encrypted_data, nonce, algorithm)?;
220		let mut cursor = Cursor::new(decrypted_data);
221		let (file_type,
222			filename,
223			parent_file_number,
224			atime,
225			mtime,
226			ctime,
227			btime,
228			metadata_ext) = Self::decode_inner_content(&mut cursor)?;
229		let file_header = Self::new(
230			header_version,
231			file_number,
232			file_type,
233			filename,
234			parent_file_number,
235			atime,
236			mtime,
237			ctime,
238			btime,
239			metadata_ext);
240		Ok(file_header)
241	}
242
243	#[allow(clippy::type_complexity)]
244	fn decode_inner_content<R: Read>(inner_content: &mut R) -> Result<(
245		FileType,
246		String, //Filename
247		u64, //parent_file_number
248		u64, //atime,
249		u64, //mtime
250		u64, //ctime,
251		u64, //btime,
252		HashMap<String, String>,
253		)> {
254		let file_type = match u8::decode_directly(inner_content)? {
255			1 => FileType::File,
256			2 => FileType::Directory,
257			3 => FileType::Symlink,
258			4 => FileType::Hardlink,
259			val => return Err(ZffError::new(ZffErrorKind::UnknownFileType, val.to_string()))
260		};
261		let filename = String::decode_directly(inner_content)?;
262		let parent_file_number = u64::decode_directly(inner_content)?;
263		let atime = u64::decode_directly(inner_content)?;
264		let mtime = u64::decode_directly(inner_content)?;
265		let ctime = u64::decode_directly(inner_content)?;
266		let btime = u64::decode_directly(inner_content)?;
267		let metadata_ext = HashMap::<String, String>::decode_directly(inner_content)?;
268
269		let inner_content = (
270			file_type,
271			filename,
272			parent_file_number,
273			atime,
274			mtime,
275			ctime,
276			btime,
277			metadata_ext);
278		Ok(inner_content)
279	}
280}
281
282impl HeaderCoding for FileHeader {
283	type Item = FileHeader;
284	
285	fn identifier() -> u32 {
286		HEADER_IDENTIFIER_FILE_HEADER
287	}
288
289	fn version(&self) -> u8 {
290		self.version
291	}
292	
293	fn encode_header(&self) -> Vec<u8> {
294		let mut vec = Vec::new();
295		vec.append(&mut self.version.encode_directly());
296		vec.append(&mut self.file_number.encode_directly());
297		vec.append(&mut self.encode_content());
298		vec
299		
300	}
301
302	fn decode_content(data: Vec<u8>) -> Result<FileHeader> {
303		let mut cursor = Cursor::new(data);
304		let header_version = u8::decode_directly(&mut cursor)?;
305		let file_number = u64::decode_directly(&mut cursor)?;
306		let (file_type, filename, parent_file_number, atime, mtime, ctime, btime, metadata_ext) = Self::decode_inner_content(&mut cursor)?;
307		Ok(FileHeader::new(header_version, file_number, file_type, filename, parent_file_number, atime, mtime, ctime, btime, metadata_ext))
308	}
309}