1 /*********************************************************************/
2 // dar - disk archive - a backup/restoration program
3 // Copyright (C) 2002-2052 Denis Corbin
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 //
19 // to contact the author : http://dar.linux.free.fr/email.html
20 /*********************************************************************/
21 
22 #include "../my_config.h"
23 
24 extern "C"
25 {
26 #if HAVE_STRING_H
27 #include <string.h>
28 #endif
29 } // end of extern "C"
30 
31 
32 #include "header_version.hpp"
33 #include "integers.hpp"
34 
35 #define LIBDAR_URL_VERSION "http://dar.linux.free.fr/pre-release/doc/Notes.html#Dar_version_naming"
36 
37 using namespace std;
38 
39 namespace libdar
40 {
41     static const U_I HEADER_CRC_SIZE = 2; //< CRC width (deprecated, now only used when reading old archives)
42 
header_version()43     header_version::header_version()
44     {
45 	algo_zip = none;
46 	cmd_line = "";
47 	initial_offset = 0;
48 	sym = crypto_none;
49 	crypted_key = nullptr;
50 	ref_layout = nullptr;
51 	ciphered = false;
52 	arch_signed = false;
53     }
54 
read(generic_file & f,user_interaction & dialog,bool lax_mode)55     void header_version::read(generic_file & f, user_interaction & dialog, bool lax_mode)
56     {
57 	crc *ctrl = nullptr;
58 	char tmp;
59         U_I flag;
60 
61 	f.reset_crc(HEADER_CRC_SIZE);
62 	try
63 	{
64 	    edition.read(f);
65 	}
66 	catch(Egeneric & e)
67 	{
68 	    if(lax_mode)
69 	    {
70 		string answ;
71 		U_I equivalent;
72 		bool ok = false;
73 
74 		dialog.warning(gettext("LAX MODE: Failed to read the archive header's format version."));
75 		do
76 		{
77 		    answ = dialog.get_string(tools_printf(gettext("LAX MODE: Please provide the archive format: You can use the table at %s to find the archive format depending on the release version, (for example if this archive has been created using dar release 2.3.4 to 2.3.7 answer \"6\" without the quotes here): "), LIBDAR_URL_VERSION), true);
78 		    if(tools_my_atoi(answ.c_str(), equivalent))
79 			edition = equivalent;
80 		    else
81 		    {
82 			dialog.pause(tools_printf(gettext("LAX MODE: \"%S\" is not a valid archive format"), &answ));
83 			continue;
84 		    }
85 
86 		    try
87 		    {
88 			dialog.pause(tools_printf(gettext("LAX MODE: Using archive format \"%d\"?"), equivalent));
89 			ok = true;
90 		    }
91 		    catch(Euser_abort & e)
92 		    {
93 			ok = false;
94 		    }
95 		}
96 		while(!ok);
97 	    }
98 	    else
99 		throw;
100 	}
101 
102 	if(f.read(&tmp, 1) == 1) // compression algo
103 	{
104 	    bool ok = false;
105 	    do
106 	    {
107 		try
108 		{
109 		    algo_zip = char2compression(tmp);
110 		    ok = true;
111 		}
112 		catch(Erange & e)
113 		{
114 		    if(!lax_mode)
115 			throw;
116 
117 		    string answ = dialog.get_string(gettext("LAX MODE: Unknown compression algorithm used, assuming data corruption occurred. Please help me, answering with one of the following words \"none\", \"gzip\", \"bzip2\" or \"lzo\" at the next prompt:"), true);
118 		    if(answ == gettext("none"))
119 			tmp = compression2char(none);
120 		    else if(answ == gettext("gzip"))
121 			tmp = compression2char(gzip);
122 		    else if(answ == gettext("bzip2"))
123 			tmp = compression2char(bzip2);
124 		    else if(answ == gettext("lzo"))
125 			tmp = compression2char(lzo);
126 		}
127 	    }
128 	    while(!ok);
129 	}
130 	else
131 	    throw Erange("header_version::read", gettext("Reached End of File while reading archive header_version data structure"));
132 
133 	tools_read_string(f, cmd_line);
134 
135 	if(edition > 1)
136 	{
137 	    unsigned char tomp;
138 	    if(f.read((char *)&tomp, 1) != 1)
139 		throw Erange("header_version::read", gettext("Reached End of File while reading archive header_version data structure"));
140 		// even in lax mode, because reading further is vain
141 	    flag = tomp;
142 	}
143 	else
144 	    flag = 0; // flag has been at edition 2
145 	if((flag & FLAG_HAS_AN_EXTENDED_SIZE) != 0)
146 	{
147 	    unsigned char tomp;
148 
149 	    if(f.read((char *)&tomp, 1) != 1)
150 		throw Erange("header_version::read", gettext("Reached End of File while reading archive header_version data structure"));
151 	    flag <<= 8;
152 	    flag += tomp;
153 	}
154 
155 	if((flag & FLAG_INITIAL_OFFSET) != 0)
156 	{
157 	    initial_offset.read(f);
158 	}
159 	else
160 	    initial_offset = 0;
161 
162 	if((flag & FLAG_SCRAMBLED) != 0)
163 	{
164 	    ciphered = true;
165 	    if(edition >= 9)
166 	    {
167 		if(f.read(&tmp, sizeof(tmp)) != 1)
168 		    throw Erange("header_version::read", gettext("Reached End of File while reading archive header_version data structure"));
169 
170 		try
171 		{
172 		    sym = char_2_crypto_algo(tmp);
173 		}
174 		catch(Erange & e)
175 		{
176 		    if(!lax_mode)
177 			throw;
178 		    dialog.printf("Unknown crypto algorithm used in archive, ignoring that field and simply assuming the archive has been encrypted, if not done you will need to specify the crypto algorithm to use in order to read this archive");
179 		    sym = crypto_none;
180 		}
181 	    }
182 	    else
183 		    // unknown ciphering algorithm used (old archive format) or no encryption
184 		    // not coherent with flag which has the FLAG_SCRAMBLED bit set
185 		    // but that this way we record that the crypto algo has
186 		    // to be provided by the user
187 		sym = crypto_none;
188 	}
189 	else
190 	{
191 	    ciphered = false;
192 	    sym = crypto_none; // no crypto used, coherent with flag
193 	}
194 
195 	has_tape_marks = (flag & FLAG_SEQUENCE_MARK) != 0;
196 	if(edition < 8 && has_tape_marks)
197 	{
198 	    if(lax_mode)
199 		has_tape_marks = false; // Escape sequence marks appeared at revision 08
200 	    else
201 		throw Erange("header_version::read", gettext("Corruption met while reading header_version data structure"));
202 	}
203 
204 	if(crypted_key != nullptr)
205 	{
206 	    delete crypted_key;
207 	    crypted_key = nullptr;
208 	}
209 
210 	if((flag & FLAG_HAS_CRYPTED_KEY) != 0)
211 	{
212 	    infinint key_size = f;
213 
214 	    crypted_key = new (get_pool()) memory_file();
215 	    if(crypted_key == nullptr)
216 		throw Ememory("header_version::read");
217 	    if(f.copy_to(*crypted_key, key_size) != key_size)
218 		throw Erange("header_version::read", gettext("Missing data for encrypted symmetrical key"));
219 	}
220 
221 	if((flag & FLAG_HAS_REF_SLICING) != 0)
222 	{
223 	    try
224 	    {
225 		if(ref_layout == nullptr)
226 		    ref_layout = new (get_pool()) slice_layout();
227 		if(ref_layout == nullptr)
228 		    throw Ememory("header_version::read");
229 		ref_layout->read(f);
230 	    }
231 	    catch(Egeneric & e)
232 	    {
233 		if(lax_mode)
234 		{
235 		    dialog.warning(gettext("Error met while reading archive of reference slicing layout, ignoring this field and continuing"));
236 		    clear_slice_layout();
237 		}
238 		else
239 		    throw;
240 	    }
241 	}
242 	else
243 	    clear_slice_layout();
244 
245 	arch_signed = (flag & FLAG_ARCHIVE_IS_SIGNED) != 0;
246 
247 	ctrl = f.get_crc();
248 	if(ctrl == nullptr)
249 	    throw SRC_BUG;
250 
251 	try
252 	{
253 	    if(edition == empty_archive_version())
254 	    {
255 		if(lax_mode)
256 		    dialog.warning(gettext("Consistency check failed for archive header"));
257 		else
258 		    throw Erange("header_version::read", gettext("Consistency check failed for archive header"));
259 	    }
260 
261 	    if(edition > 7)
262 	    {
263 		crc *coh = create_crc_from_file(f, get_pool());
264 
265 		if(coh == nullptr)
266 		    throw SRC_BUG;
267 		try
268 		{
269 		    if(typeid(*coh) != typeid(*ctrl))
270 		    {
271 			if(coh->get_size() != ctrl->get_size())
272 			    throw SRC_BUG;
273 			else
274 			    throw SRC_BUG; // both case lead to a bug, but we need to know which one is met
275 		    }
276 
277 		    if(*coh != *ctrl)
278 		    {
279 			if(lax_mode)
280 			    dialog.warning(gettext("Consistency check failed for archive header"));
281 			else
282 			    throw Erange("header_version::read", gettext("Consistency check failed for archive header"));
283 		    }
284 		}
285 		catch(...)
286 		{
287 		    if(coh != nullptr)
288 			delete coh;
289 		    throw;
290 		}
291 		if(coh != nullptr)
292 		    delete coh;
293 	    }
294 	    if(initial_offset.is_zero())
295 		initial_offset = f.get_position();
296 	}
297 	catch(...)
298 	{
299 	    if(ctrl != nullptr)
300 		delete ctrl;
301 	    throw;
302 	}
303 
304 	if(ctrl != nullptr)
305 	    delete ctrl;
306     }
307 
write(generic_file & f) const308     void header_version::write(generic_file &f) const
309     {
310 	crc *ctrl = nullptr;
311 	char tmp;
312         unsigned char flag[2];
313 
314 	    // preparing the data
315 
316 	flag[0] = 0;
317 	flag[1] = 0;
318 
319 	if(!initial_offset.is_zero())
320 	    flag[0] |= FLAG_INITIAL_OFFSET; // adding it to the flag
321 
322 	if(crypted_key != nullptr)
323 	    flag[0] |= FLAG_HAS_CRYPTED_KEY;
324 
325 	if(ref_layout != nullptr)
326 	    flag[0] |= FLAG_HAS_REF_SLICING;
327 
328 	if(has_tape_marks)
329 	    flag[0] |= FLAG_SEQUENCE_MARK;
330 
331 	if(sym != crypto_none)
332 	    flag[0] |= FLAG_SCRAMBLED;
333 	    // Note: we cannot set this flag (even if ciphered is true) if we do not know the crypto algo
334 	    // as since version 9 the presence of this flag implies the existence
335 	    // of the crypto algorithm in the header/trailer and we will always
336 	    // write down a header/version of the latest known format (thus greater or
337 	    // equal to 9).
338 
339 	if(arch_signed)
340 	    flag[1] |= (FLAG_ARCHIVE_IS_SIGNED >> 8);
341 
342 	if(flag[1] > 0)
343 	    flag[1] |= FLAG_HAS_AN_EXTENDED_SIZE;
344 	    // and we will drop two bytes for the flag
345 
346 	    // writing down the data
347 
348 	f.reset_crc(HEADER_CRC_SIZE);
349 	edition.dump(f);
350 	tmp = compression2char(algo_zip);
351 	f.write(&tmp, sizeof(tmp));
352 	tools_write_string(f, cmd_line);
353 	if(flag[1] != 0)
354 	    f.write((char *)&(flag[1]), sizeof(unsigned char));
355 	f.write((char *)&(flag[0]), sizeof(unsigned char));
356 	if(initial_offset != 0)
357 	    initial_offset.dump(f);
358 	if(sym != crypto_none)
359 	{
360 	    tmp = crypto_algo_2_char(sym);
361 	    f.write(&tmp, sizeof(tmp));
362 	}
363 
364 	if(crypted_key != nullptr)
365 	{
366 	    crypted_key->size().dump(f);
367 	    crypted_key->skip(0);
368 	    crypted_key->copy_to(f);
369 	}
370 
371 	if(ref_layout != nullptr)
372 	    ref_layout->write(f);
373 
374 	ctrl = f.get_crc();
375 	if(ctrl == nullptr)
376 	    throw SRC_BUG;
377 	try
378 	{
379 	    ctrl->dump(f);
380 	}
381 	catch(...)
382 	{
383 	    if(ctrl != nullptr)
384 		delete ctrl;
385 	    throw;
386 	}
387 	if(ctrl != nullptr)
388 	    delete ctrl;
389     }
390 
display(user_interaction & dialog) const391     void header_version::display(user_interaction & dialog) const
392     {
393 	string algo = compression2string(get_compression_algo());
394 	string sym = get_edition() >= 9 ? crypto_algo_2_string(get_sym_crypto_algo()) : (is_ciphered() ? gettext("yes") : gettext("no"));
395 	string asym = get_edition() >= 9 && (get_crypted_key() != nullptr) ? "gnupg" : gettext("none");
396 	string xsigned = is_signed() ? gettext("yes") : gettext("no");
397 
398 	dialog.printf(gettext("Archive version format               : %s\n"), get_edition().display().c_str());
399 	dialog.printf(gettext("Compression algorithm used           : %S\n"), &algo);
400 	dialog.printf(gettext("Symmetric key encryption used        : %S\n"), &sym);
401 	dialog.printf(gettext("Asymmetric key encryption used       : %S\n"), &asym);
402 	dialog.printf(gettext("Archive is signed                    : %S\n"), &xsigned);
403 	dialog.printf(gettext("Sequential reading marks             : %s\n"), (get_tape_marks() ? gettext("present") : gettext("absent")));
404 	dialog.printf(gettext("User comment                         : %S\n"), &(get_command_line()));
405     }
406 
detruit()407     void header_version::detruit()
408     {
409 	clear_crypted_key();
410 	clear_slice_layout();
411     }
412 
copy_from(const header_version & ref)413     void header_version::copy_from(const header_version & ref)
414     {
415 	edition = ref.edition;
416 	algo_zip = ref.algo_zip;
417 	cmd_line = ref.cmd_line;
418 	initial_offset = ref.initial_offset;
419 	sym = ref.sym;
420 	if(ref.crypted_key != nullptr)
421 	{
422 	    crypted_key = new (get_pool()) memory_file(*ref.crypted_key);
423 	    if(crypted_key == nullptr)
424 		throw Ememory("header_version::copy_from");
425 	}
426 	else
427 	    crypted_key = nullptr;
428 	if(ref.ref_layout != nullptr)
429 	{
430 	    ref_layout = new (get_pool()) slice_layout(*ref.ref_layout);
431 	    if(ref_layout == nullptr)
432 		throw Ememory("header_version::copy_from");
433 	}
434 	else
435 	    ref_layout = nullptr;
436 	has_tape_marks = ref.has_tape_marks;
437 	ciphered = ref.ciphered;
438 	arch_signed = ref.arch_signed;
439     }
440 
441 
442 
443 } // end of namespace
444