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 STDC_HEADERS
27 #include <stdlib.h>
28 #endif
29 
30 #if HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 } // end extern "C"
34 
35 #include <iomanip>
36 #include <iostream>
37 #include <deque>
38 #include "database.hpp"
39 #include "user_interaction.hpp"
40 #include "deci.hpp"
41 #include "tools.hpp"
42 #include "storage.hpp"
43 #include "database_header.hpp"
44 
45 using namespace libdar;
46 using namespace std;
47 
48 static storage *file2storage(generic_file &f, memory_pool *pool);
49 static void memory2file(storage &s, generic_file &f);
50 
51 namespace libdar
52 {
53 
database()54     database::database()
55     {
56 	archive_data dat;
57 
58 	dat.chemin = "";
59 	dat.basename = "";
60 	coordinate.clear();
61 	coordinate.push_back(dat); // coordinate[0] is never used, but must exist
62 	options_to_dar.clear();
63 	dar_path = "";
64 	files = new (get_pool()) data_dir("."); // "." or whaterver else (this name is not used)
65 	if(files == nullptr)
66 	    throw Ememory("database::database");
67 	data_files = nullptr;
68 	check_order_asked = true;
69 	cur_db_version = database_header_get_supported_version();
70     }
71 
database(user_interaction & dialog,const string & base,const database_open_options & opt)72     database::database(user_interaction & dialog, const string & base, const database_open_options & opt)
73     {
74 	generic_file *f = database_header_open(dialog, get_pool(), base, cur_db_version);
75 
76 	if(f == nullptr)
77 	    throw Ememory("database::database");
78 	try
79 	{
80 	    check_order_asked = opt.get_warn_order();
81 	    build(dialog, *f, opt.get_partial(), opt.get_partial_read_only(), cur_db_version);
82 	}
83 	catch(...)
84 	{
85 	    delete f;
86 	    throw;
87 	}
88 	delete f;
89     }
90 
build(user_interaction & dialog,generic_file & f,bool partial,bool read_only,const unsigned char db_version)91     void database::build(user_interaction & dialog, generic_file & f, bool partial, bool read_only, const unsigned char db_version)
92     {
93 	NLS_SWAP_IN;
94 	try
95 	{
96 	    struct archive_data dat;
97 
98 	    if(db_version > database_header_get_supported_version())
99 		throw SRC_BUG; // we should not get there if the database is more recent than what that software can handle. this is necessary if we do not want to destroy the database or loose data.
100 	    coordinate.clear();
101 	    infinint tmp = infinint(f); // number of archive to read
102 	    while(!tmp.is_zero())
103 	    {
104 		tools_read_string(f, dat.chemin);
105 		tools_read_string(f, dat.basename);
106 		if(db_version >= 3)
107 		    dat.root_last_mod.read(f, db2archive_version(db_version));
108 		else
109 		    dat.root_last_mod = datetime(0);
110 		coordinate.push_back(dat);
111 		--tmp;
112 	    }
113 	    if(coordinate.empty())
114 		throw Erange("database::database", gettext("Badly formatted database"));
115 	    tools_read_vector(f, options_to_dar);
116 	    tools_read_string(f, dar_path);
117 	    if(db_version < database_header_get_supported_version())
118 		partial = false;
119 
120 	    if(!partial)
121 	    {
122 		files = data_tree_read(f, db_version, get_pool());
123 		if(files == nullptr)
124 		    throw Ememory("database::database");
125 		if(files->get_name() != ".")
126 		    files->set_name(".");
127 		data_files = nullptr;
128 	    }
129 	    else
130 	    {
131 		if(!read_only)
132 		{
133 		    files = nullptr;
134 		    data_files = file2storage(f, get_pool());
135 		}
136 		else
137 		{
138 		    files = nullptr;
139 		    data_files = nullptr;
140 		}
141 	    }
142 	}
143 	catch(...)
144 	{
145 	    NLS_SWAP_OUT;
146 	    throw;
147 	}
148 	NLS_SWAP_OUT;
149     }
150 
~database()151     database::~database()
152     {
153 	if(files != nullptr)
154 	    delete files;
155 	if(data_files != nullptr)
156 	    delete data_files;
157     }
158 
dump(user_interaction & dialog,const std::string & filename,const database_dump_options & opt) const159     void database::dump(user_interaction & dialog, const std::string & filename, const database_dump_options & opt) const
160     {
161 	if(files == nullptr && data_files == nullptr)
162 	    throw Erange("database::dump", gettext("Cannot write down a read-only database"));
163 
164 	generic_file *f = database_header_create(dialog, get_pool(), filename, opt.get_overwrite());
165 	if(f == nullptr)
166 	    throw Ememory("database::dump");
167 
168 	try
169 	{
170 	    archive_num tmp = coordinate.size();
171 
172  	    infinint(tmp).dump(*f);
173 	    for(archive_num i = 0; i < tmp; ++i)
174 	    {
175 		tools_write_string(*f, coordinate[i].chemin);
176 		tools_write_string(*f, coordinate[i].basename);
177 		coordinate[i].root_last_mod.dump(*f);
178 	    }
179 	    tools_write_vector(*f, options_to_dar);
180 	    tools_write_string(*f, dar_path);
181 	    if(files != nullptr)
182 		files->dump(*f);
183 	    else
184 		if(data_files != nullptr)
185 		    memory2file(*data_files, *f);
186 		else
187 		    throw SRC_BUG;
188 	}
189 	catch(...)
190 	{
191 	    if(f != nullptr)
192 		delete f;
193 	    throw;
194 	}
195 	if(f != nullptr)
196 	    delete f;
197     }
198 
add_archive(const archive & arch,const string & chemin,const string & basename,const database_add_options & opt)199     void database::add_archive(const archive & arch, const string & chemin, const string & basename, const database_add_options & opt)
200     {
201 	NLS_SWAP_IN;
202 	try
203 	{
204 	    struct archive_data dat;
205 	    archive_num number = coordinate.size();
206 
207 	    if(files == nullptr)
208 		throw SRC_BUG;
209 	    if(basename == "")
210 		throw Erange("database::add_archive", gettext("Empty string is an invalid archive basename"));
211 	    if(number >= ARCHIVE_NUM_MAX)
212 		throw Erange("database::add_archive", gettext("Cannot add another archive, database is full"));
213 
214 	    dat.chemin = chemin;
215 	    dat.basename = basename;
216 	    dat.root_last_mod = arch.get_catalogue().get_root_dir_last_modif();
217 	    coordinate.push_back(dat);
218 	    data_tree_update_with(arch.get_catalogue().get_contenu(), number, files);
219 	    if(number > 1)
220 		files->finalize_except_self(number, get_root_last_mod(number), 0);
221 	}
222 	catch(...)
223 	{
224 	    NLS_SWAP_OUT;
225 	    throw;
226 	}
227 	NLS_SWAP_OUT;
228     }
229 
remove_archive(archive_num min,archive_num max,const database_remove_options & opt)230     void database::remove_archive(archive_num min, archive_num max, const database_remove_options & opt)
231     {
232 	NLS_SWAP_IN;
233 	try
234 	{
235 	    min = get_real_archive_num(min, opt.get_revert_archive_numbering());
236 	    max = get_real_archive_num(max, opt.get_revert_archive_numbering());
237 	    if(min > max)
238 		throw Erange("database::remove_archive", gettext("Incorrect archive range in database"));
239 	    if(min == 0 || max >= coordinate.size())
240 		throw Erange("database::remove_archive", gettext("Incorrect archive range in database"));
241 	    for(U_I i = max ; i >= min ; --i)
242 	    {
243 		if(files == nullptr)
244 		    throw SRC_BUG;
245 		files->remove_all_from(i, coordinate.size() - 1);
246 		files->skip_out(i);
247 		coordinate.erase(coordinate.begin() + i);
248 	    }
249 	}
250 	catch(...)
251 	{
252 	    NLS_SWAP_OUT;
253 	    throw;
254 	}
255 	NLS_SWAP_OUT;
256     }
257 
change_name(archive_num num,const string & basename,const database_change_basename_options & opt)258     void database::change_name(archive_num num, const string & basename, const database_change_basename_options &opt)
259     {
260 	NLS_SWAP_IN;
261 	try
262 	{
263 	    num = get_real_archive_num(num, opt.get_revert_archive_numbering());
264 	    if(num < coordinate.size() && num != 0)
265 		coordinate[num].basename = basename;
266 	    else
267 		throw Erange("database::change_name", gettext("Non existent archive in database"));
268 	}
269 	catch(...)
270 	{
271 	    NLS_SWAP_OUT;
272 	    throw;
273 	}
274 	NLS_SWAP_OUT;
275     }
276 
set_path(archive_num num,const string & chemin,const database_change_path_options & opt)277     void database::set_path(archive_num num, const string & chemin, const database_change_path_options & opt)
278     {
279 	NLS_SWAP_IN;
280 	try
281 	{
282 	    num = get_real_archive_num(num, opt.get_revert_archive_numbering());
283 	    if(num < coordinate.size() && coordinate[num].basename != "")
284 		coordinate[num].chemin = chemin;
285 	    else
286 		throw Erange("database::change_name", gettext("Non existent archive in database"));
287 	}
288 	catch(...)
289 	{
290 	    NLS_SWAP_OUT;
291 	    throw;
292 	}
293 	NLS_SWAP_OUT;
294     }
295 
set_permutation(archive_num src,archive_num dst)296     void database::set_permutation(archive_num src, archive_num dst)
297     {
298 	NLS_SWAP_IN;
299 	try
300 	{
301 	    struct archive_data moved;
302 
303 	    if(files == nullptr)
304 		throw SRC_BUG;
305 	    if(src >= coordinate.size() || src <= 0)
306 		throw Erange("database::set_permutation", string(gettext("Invalid archive number: ")) + tools_int2str(src));
307 	    if(dst >= coordinate.size() || dst <= 0)
308 		throw Erange("database::set_permutation", string(gettext("Invalid archive number: ")) + tools_int2str(dst));
309 
310 	    moved = coordinate[src];
311 	    coordinate.erase(coordinate.begin()+src);
312 	    coordinate.insert(coordinate.begin()+dst, moved);
313 	    files->apply_permutation(src, dst);
314 
315 		// update et_absent dates
316 
317 	    set<archive_num> re_finalize;
318 	    set<archive_num>::iterator re_it;
319 
320 	    if(src < dst)
321 	    {
322 		re_finalize.insert(src);
323 		re_finalize.insert(dst);
324 		if(dst+1 < (archive_num)coordinate.size())
325 		    re_finalize.insert(dst+1);
326 	    }
327 	    else // src >= dst
328 	    {
329 		if(src+1 < (archive_num)coordinate.size())
330 		    re_finalize.insert(src+1);
331 		re_finalize.insert(dst);
332 		if(dst+1 < (archive_num)coordinate.size())
333 		    re_finalize.insert(dst+1);
334 
335 		    // if src == dst the set still contains on entry (src or dst).
336 		    // this is intended to let the user have the possibility
337 		    // to ask dates recompilation, even if in theory this is useless
338 	    }
339 
340 	    re_it = re_finalize.begin();
341 	    while(re_it != re_finalize.end())
342 	    {
343 		files->finalize_except_self(*re_it, get_root_last_mod(*re_it), *re_it + 1);
344 		++re_it;
345 	    }
346 
347 	}
348         catch(...)
349         {
350             NLS_SWAP_OUT;
351             throw;
352         }
353         NLS_SWAP_OUT;
354     }
355 
show_contents(user_interaction & dialog) const356     void database::show_contents(user_interaction & dialog) const
357     {
358 	NLS_SWAP_IN;
359 	try
360 	{
361 	    string opt = tools_concat_vector(" ", options_to_dar);
362 
363 	    if(!dialog.get_use_dar_manager_contents())
364 	    {
365 		dialog.warning("\n");
366 		dialog.printf(gettext("dar path        : %S\n"), &dar_path);
367 		dialog.printf(gettext("dar options     : %S\n"), &opt);
368 		dialog.printf(gettext("database version: %d\n"), cur_db_version);
369 		dialog.warning("\n");
370 		dialog.printf(gettext("archive #   |    path      |    basename\n"));
371 		dialog.printf("------------+--------------+---------------\n");
372 	    }
373 
374 	    for(archive_num i = 1; i < coordinate.size(); ++i)
375 	    {
376 		if(dialog.get_use_dar_manager_contents())
377 		    dialog.dar_manager_contents(i, coordinate[i].chemin, coordinate[i].basename);
378 		else
379 		{
380 		    opt = (coordinate[i].chemin == "") ? gettext("<empty>") : coordinate[i].chemin;
381 		    dialog.printf(" \t%u\t%S\t%S\n", i, &opt, &coordinate[i].basename);
382 		}
383 	    }
384 	}
385 	catch(...)
386 	{
387 	    NLS_SWAP_OUT;
388 	    throw;
389 	}
390 	NLS_SWAP_OUT;
391     }
392 
show_files(user_interaction & dialog,archive_num num,const database_used_options & opt) const393     void database::show_files(user_interaction & dialog, archive_num num, const database_used_options & opt) const
394     {
395 	NLS_SWAP_IN;
396 	try
397 	{
398 	    if(num != 0)
399 		num = get_real_archive_num(num, opt.get_revert_archive_numbering());
400 	    if(files == nullptr)
401 		throw SRC_BUG;
402 
403 	    if(num < coordinate.size())
404 		files->show(dialog, num);
405 	    else
406 		throw Erange("database::show_files", gettext("Non existent archive in database"));
407 	}
408 	catch(...)
409 	{
410 	    NLS_SWAP_OUT;
411 	    throw;
412 	}
413 	NLS_SWAP_OUT;
414     }
415 
416 
show_version(user_interaction & dialog,path chemin) const417     void database::show_version(user_interaction & dialog, path chemin) const
418     {
419 	NLS_SWAP_IN;
420 	try
421 	{
422 	    const data_tree *ptr = nullptr;
423 	    const data_dir *ptr_dir = files;
424 	    string tmp;
425 
426 	    if(files == nullptr)
427 		throw SRC_BUG;
428 
429 	    if(!chemin.is_relative())
430 		throw Erange("database::show_version", gettext("Invalid path, path must be relative"));
431 
432 	    while(chemin.pop_front(tmp) && ptr_dir != nullptr)
433 	    {
434 		ptr = ptr_dir->read_child(tmp);
435 		if(ptr == nullptr)
436 		    throw Erange("database::show_version", gettext("Non existent file in database"));
437 		ptr_dir = dynamic_cast<const data_dir *>(ptr);
438 	    }
439 
440 	    if(ptr_dir == nullptr)
441 		throw Erange("database::show_version", gettext("Non existent file in database"));
442 
443 	    ptr = ptr_dir->read_child(chemin.display());
444 	    if(ptr == nullptr)
445 		throw Erange("database::show_version", gettext("Non existent file in database"));
446 	    else
447 		ptr->listing(dialog);
448 	}
449 	catch(...)
450 	{
451 	    NLS_SWAP_OUT;
452 	    throw;
453 	}
454 	NLS_SWAP_OUT;
455     }
456 
show_most_recent_stats(user_interaction & dialog) const457     void database::show_most_recent_stats(user_interaction & dialog) const
458     {
459 	NLS_SWAP_IN;
460 	try
461 	{
462 	    vector<infinint> stats_data(coordinate.size(), 0);
463 	    vector<infinint> stats_ea(coordinate.size(), 0);
464 	    vector<infinint> total_data(coordinate.size(), 0);
465 	    vector<infinint> total_ea(coordinate.size(), 0);
466 	    if(files == nullptr)
467 		throw SRC_BUG;
468 	    files->compute_most_recent_stats(stats_data, stats_ea, total_data, total_ea);
469 
470 	    if(!dialog.get_use_dar_manager_statistics())
471 	    {
472 		dialog.printf(gettext("  archive #   |  most recent/total data |  most recent/total EA\n"));
473 		dialog.printf(gettext("--------------+-------------------------+-----------------------\n")); // having it with gettext let the translater adjust columns width
474 	    }
475 	    for(archive_num i = 1; i < coordinate.size(); ++i)
476 		if(dialog.get_use_dar_manager_statistics())
477 		    dialog.dar_manager_statistics(i, stats_data[i], total_data[i], stats_ea[i], total_ea[i]);
478 		else
479 		    dialog.printf("\t%u %i/%i \t\t\t %i/%i\n", i, &stats_data[i], &total_data[i], &stats_ea[i], &total_ea[i]);
480 	}
481 	catch(...)
482 	{
483 	    NLS_SWAP_OUT;
484 	    throw;
485 	}
486 	NLS_SWAP_OUT;
487     }
488 
489 
restore(user_interaction & dialog,const vector<string> & filename,const database_restore_options & opt)490     void database::restore(user_interaction & dialog,
491 			   const vector<string> & filename,
492 			   const database_restore_options & opt)
493     {
494 	NLS_SWAP_IN;
495 	try
496 	{
497 	    map<archive_num, vector<string> > command_line;
498 	    deque<string> anneau;
499 	    const data_tree *ptr;
500 
501 	    anneau.assign(filename.begin(), filename.end());
502 	    if(files == nullptr)
503 		throw SRC_BUG;
504 
505 	    if(opt.get_info_details())
506 		dialog.warning(gettext("Checking chronological ordering of files between the archives..."));
507 	    check_order(dialog);
508 
509 		// determination of the archive to restore and files to restore for each selected file
510 	    while(!anneau.empty())
511 	    {
512 		if(data_tree_find(anneau.front(), *files, ptr))
513 		{
514 		    const data_dir *ptr_dir = dynamic_cast<const data_dir *>(ptr);
515 		    archive_num num_data = 0;
516 		    archive_num num_ea = 0;
517 		    data_tree::lookup look_ea, look_data;
518 
519 		    look_data = ptr->get_data(num_data, opt.get_date(), opt.get_even_when_removed());
520 		    look_ea = ptr->get_EA(num_ea, opt.get_date(), opt.get_even_when_removed());
521 
522 		    switch(look_data)
523 		    {
524 		    case data_tree::found_present:
525 			break;
526 		    case data_tree::found_removed:
527 			num_data = 0; // we do not restore it
528 			if(opt.get_info_details())
529 			    dialog.warning(string(gettext("File recorded as removed at this date in database: ")) + anneau.front());
530 			break;
531 		    case data_tree::not_found:
532 			num_data = 0;
533 			dialog.warning(string(gettext("File not found in database: ")) + anneau.front());
534 			break;
535 		    case data_tree::not_restorable:
536 			num_data = 0;
537 			dialog.warning(string(gettext("File found in database but impossible to restore (only found \"unchanged\" in differential backups): ")) + anneau.front());
538 			break;
539 		    default:
540 			throw SRC_BUG;
541 		    }
542 
543 		    switch(look_ea)
544 		    {
545 		    case data_tree::found_present:
546 			if(opt.get_even_when_removed()
547 			   && look_data == data_tree::found_present
548 			   && num_data > num_ea)
549 			    num_ea = num_data;
550 			break;
551 		    case data_tree::found_removed:
552 			num_ea = 0; // we do not restore it
553 			break;
554 		    case data_tree::not_found:
555 			num_ea = 0;
556 			break;
557 		    case data_tree::not_restorable:
558 			num_ea = 0;
559 			dialog.warning(string(gettext("Extended Attribute of file found in database but impossible to restore (only found \"unchanged\" in differential backups): ")) + anneau.front());
560 			break;
561 		    default:
562 			throw SRC_BUG;
563 		    }
564 
565 			// if there is something to restore for that file
566 
567 		    if(look_ea == data_tree::found_present || look_data == data_tree::found_present)
568 		    {
569 			if(num_data == num_ea) // both EA and data are located in the same archive
570 			{
571 			    if(num_data != 0) // archive is not zero (so it is a real archive)
572 			    {
573 				command_line[num_data].push_back("-g");
574 				command_line[num_data].push_back(anneau.front());
575 			    }
576 			    else // archive number is zero (not a valid archive number)
577 				if(!opt.get_date().is_zero()) // a date was specified
578 				{
579 				    string fic = anneau.front();
580 				    if(opt.get_info_details())
581 					dialog.printf(gettext("%S did not exist before specified date and cannot be restored"), &fic);
582 				}
583 				else
584 				    throw SRC_BUG; // no date limitation, the file's data should have been found
585 			}
586 			else // num_data != num_ea
587 			{
588 			    if(num_data != 0)
589 			    {
590 				command_line[num_data].push_back("-g");
591 				command_line[num_data].push_back(anneau.front());
592 			    }
593 			    if(num_ea != 0)
594 			    {
595 				command_line[num_ea].push_back("-g");
596 				command_line[num_ea].push_back(anneau.front());
597 			    }
598 			    if(num_data != 0 && num_ea != 0)
599 				if(num_data > num_ea) // will restore "EA only" then "data + old EA"
600 				{
601 				    string fic = anneau.front();
602 				    if(!opt.get_even_when_removed())
603 					dialog.printf(gettext("Either archives in database are not properly tidied, or file last modification date has been artificially set to an more ancient date. This may lead improper Extended Attribute restoration for inode %S"), &fic);
604 				}
605 			}
606 		    }
607 
608 		    if(ptr_dir != nullptr)
609 		    {  // adding current directory children in the list of files
610 			vector<string> fils;
611 			vector<string>::iterator fit;
612 			path base = anneau.front();
613 			ptr_dir->read_all_children(fils);
614 
615 			fit = fils.begin();
616 			while(fit != fils.end())
617 			    anneau.push_back((base + *(fit++)).display());
618 		    }
619 		}
620 		else
621 		{
622 		    string fic = anneau.front();
623 		    dialog.printf(gettext("Cannot restore file %S : non existent file in database"), &fic);
624 		}
625 		anneau.pop_front();
626 	    }
627 
628 		//freeing memory if early_release is set
629 
630 	    if(opt.get_early_release())
631 	    {
632 		if(files != nullptr)
633 		{
634 		    delete files;
635 		    files = nullptr;
636 		}
637 	    }
638 
639 		// calling dar for each archive
640 
641 	    if(!command_line.empty())
642 	    {
643 		string dar_cmd = dar_path != "" ? dar_path : "dar";
644 		map<archive_num, vector<string> >::iterator ut = command_line.begin();
645 		vector<string> argvector_init = vector<string>(1, dar_cmd);
646 
647 		while(ut != command_line.end())
648 		{
649 		    try
650 		    {
651 			string archive_name;
652 			vector<string> argvpipe;
653 
654 			    // building the argument list sent through anonymous pipe
655 
656 			if(coordinate[ut->first].chemin != "")
657 			    archive_name = coordinate[ut->first].chemin + "/";
658 			else
659 			    archive_name = "";
660 			archive_name += coordinate[ut->first].basename;
661 			argvpipe.push_back(dar_cmd); // just to fill the argv[0] by the command even when transmitted through anonymous pipe
662 			argvpipe.push_back("-x");
663 			argvpipe.push_back(archive_name);
664 			if(!opt.get_ignore_dar_options_in_database())
665 			    argvpipe += options_to_dar;
666 			argvpipe += opt.get_extra_options_for_dar();
667 			argvpipe += ut->second;
668 
669 			dialog.printf("CALLING DAR: restoring %d files from archive %S using anonymous pipe to transmit configuration to the dar process", ut->second.size()/2, &archive_name);
670 			if(opt.get_info_details())
671 			{
672 			    dialog.printf("Arguments sent through anonymous pipe are:");
673 			    dialog.warning(tools_concat_vector(" ", argvpipe));
674 			}
675 			tools_system_with_pipe(dialog, dar_cmd, argvpipe, get_pool());
676 		    }
677 		    catch(Erange & e)
678 		    {
679 			dialog.warning(string(gettext("Error while restoring the following files: "))
680 				       + tools_concat_vector( " ", ut->second)
681 				       + "   : "
682 				       + e.get_message());
683 		    }
684 		    ut++;
685 		}
686 	    }
687 	    else
688 		dialog.warning(gettext("Cannot restore any file, nothing done"));
689 	}
690 	catch(...)
691 	{
692 	    NLS_SWAP_OUT;
693 	    throw;
694 	}
695 	NLS_SWAP_OUT;
696     }
697 
get_real_archive_num(archive_num num,bool revert) const698     archive_num database::get_real_archive_num(archive_num num, bool revert) const
699     {
700 	if(num == 0)
701 	    throw Erange("database::get_real_archive_num", tools_printf(dar_gettext("Invalid archive number: %d"), num));
702 
703 	if(revert)
704 	{
705 	    U_I size = coordinate.size(); // size is +1 because of record zero that is never used but must exist
706 	    if(size > num)
707 		return size - num;
708 	    else
709 		throw Erange("database::get_real_archive_num", tools_printf(dar_gettext("Invalid archive number: %d"), -num));
710 	}
711 	else
712 	    return num;
713     }
714 
get_root_last_mod(const archive_num & num) const715     const datetime & database::get_root_last_mod(const archive_num & num) const
716     {
717 	if(num >= coordinate.size())
718 	    throw SRC_BUG;
719 
720 	return coordinate[num].root_last_mod;
721     }
722 
723 } // end of namespace
724 
file2storage(generic_file & f,memory_pool * pool)725 static storage *file2storage(generic_file &f, memory_pool *pool)
726 {
727     storage *st = new (pool) storage(0);
728     const U_I taille = 102400;
729     unsigned char buffer[taille];
730     S_I lu;
731 
732     if(st == nullptr)
733         throw Ememory("dar_manager:file2storage");
734 
735     do
736     {
737         lu = f.read((char *)buffer, taille);
738         if(lu > 0)
739             st->insert_bytes_at_iterator(st->end(), buffer, lu);
740     }
741     while(lu > 0);
742 
743     return st;
744 }
745 
memory2file(storage & s,generic_file & f)746 static void memory2file(storage &s, generic_file &f)
747 {
748     s.dump(f);
749 }
750