1use std::io::{Cursor, Read};
3use std::collections::HashMap;
4use std::fmt;
5
6use 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#[repr(u8)]
30#[non_exhaustive]
31#[derive(Debug,Clone,Eq,PartialEq,Hash)]
32pub enum FileType {
33 File = 1,
35 Directory = 2,
37 Symlink = 3,
39 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#[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 #[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 pub fn file_number(&self) -> u64 {
106 self.file_number
107 }
108 pub fn file_type(&self) -> FileType {
110 self.file_type.clone()
111 }
112 pub fn filename(&self) -> &str {
114 &self.filename
115 }
116 pub fn parent_file_number(&self) -> u64 {
118 self.parent_file_number
119 }
120 pub fn atime(&self) -> u64 {
122 self.atime
123 }
124 pub fn mtime(&self) -> u64 {
126 self.mtime
127 }
128 pub fn ctime(&self) -> u64 {
130 self.ctime
131 }
132 pub fn btime(&self) -> u64 {
134 self.btime
135 }
136 pub fn metadata_ext(&self) -> &HashMap<String, String> {
138 &self.metadata_ext
139 }
140
141 pub fn transform_to_hardlink(&mut self) {
143 if self.file_type != FileType::Symlink {
144 self.file_type = FileType::Hardlink
145 }
146 }
147
148 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); 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 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, u64, u64, u64, u64, u64, 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}