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 // to allow compilation under Cygwin we need <sys/types.h>
27 // else Cygwin's <netinet/in.h> lack __int16_t symbol !?!
28 #if HAVE_SYS_TYPES_H
29 #include <sys/types.h>
30 #endif
31
32 #if HAVE_NETINET_IN_H
33 #include <netinet/in.h>
34 #endif
35
36 #if HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
38 #endif
39 } // end extern "C"
40
41 #include <iomanip>
42
43 #include "data_tree.hpp"
44 #include "tools.hpp"
45 #include "user_interaction.hpp"
46 #include "deci.hpp"
47 #include "path.hpp"
48 #include "datetime.hpp"
49 #include "cat_all_entrees.hpp"
50
51 using namespace std;
52 using namespace libdar;
53
54 static data_tree *read_from_file(generic_file & f, unsigned char db_version, memory_pool *pool);
55 static void read_from_file(generic_file &f, archive_num &a);
56 static void write_to_file(generic_file &f, archive_num a);
57 static void display_line(user_interaction & dialog,
58 archive_num num,
59 const datetime *data,
60 data_tree::etat data_presence,
61 const datetime *ea,
62 data_tree::etat ea_presence);
63
64 #define ETAT_SAVED 'S'
65 #define ETAT_PRESENT 'P'
66 #define ETAT_REMOVED 'R'
67
68 namespace libdar
69 {
70
dump(generic_file & f) const71 void data_tree::status::dump(generic_file & f) const
72 {
73 date.dump(f);
74 switch(present)
75 {
76 case et_saved:
77 f.write("S", 1);
78 break;
79 case et_present:
80 f.write("P", 1);
81 break;
82 case et_removed:
83 f.write("R", 1);
84 break;
85 case et_absent:
86 f.write("A", 1);
87 break;
88 default:
89 throw SRC_BUG;
90 }
91 }
92
read(generic_file & f,unsigned char db_version)93 void data_tree::status::read(generic_file & f, unsigned char db_version)
94 {
95 char tmp;
96
97 date.read(f, db2archive_version(db_version));
98 if(f.read(&tmp, 1) != 1)
99 throw Erange("data_tree::status::read", gettext("reached End of File before all expected data could be read"));
100 switch(tmp)
101 {
102 case 'S':
103 present = et_saved;
104 break;
105 case 'P':
106 present = et_present;
107 break;
108 case 'R':
109 present = et_removed;
110 break;
111 case 'A':
112 present = et_absent;
113 break;
114 default:
115 throw Erange("data_tree::status::read", gettext("Unexpected value found in database"));
116 }
117 }
118
data_tree(const string & name)119 data_tree::data_tree(const string & name)
120 {
121 filename = name;
122 }
123
data_tree(generic_file & f,unsigned char db_version)124 data_tree::data_tree(generic_file & f, unsigned char db_version)
125 {
126 archive_num k;
127 status sta;
128
129 // signature has already been read
130 tools_read_string(f, filename);
131 infinint tmp = infinint(f); // number of entry in last_mod map
132 while(!tmp.is_zero())
133 {
134 read_from_file(f, k);
135 switch(db_version)
136 {
137 case 1:
138 sta.date = infinint(f);
139 sta.present = et_saved;
140 last_mod[k] = sta;
141 break;
142 case 2:
143 case 3:
144 case 4:
145 sta.read(f, db_version);
146 last_mod[k] = sta;
147 break;
148 default: // unsupported database version
149 throw SRC_BUG; // this statement should have been prevented earlier in the code to avoid destroying or loosing data from the database
150 }
151 --tmp;
152 }
153
154 tmp = infinint(f); // number of entry in last_change map
155 while(!tmp.is_zero())
156 {
157 read_from_file(f, k);
158 switch(db_version)
159 {
160 case 1:
161 sta.date = infinint(f);
162 sta.present = et_saved;
163 last_change[k] = sta;
164 break;
165 case 2:
166 case 3:
167 case 4:
168 sta.read(f, db_version);
169 last_change[k] = sta;
170 break;
171 default:
172 throw SRC_BUG;
173 }
174 --tmp;
175 }
176 }
177
dump(generic_file & f) const178 void data_tree::dump(generic_file & f) const
179 {
180 char tmp = obj_signature();
181 infinint sz;
182 map<archive_num, status>::const_iterator it = last_mod.begin();
183
184 f.write(&tmp, 1);
185 tools_write_string(f, filename);
186
187 sz = last_mod.size();
188 sz.dump(f);
189 while(it != last_mod.end())
190 {
191 write_to_file(f, it->first); // key
192 it->second.dump(f); // value
193 ++it;
194 }
195
196 sz = last_change.size();
197 sz.dump(f);
198 it = last_change.begin();
199 while(it != last_change.end())
200 {
201 write_to_file(f, it->first); // key
202 it->second.dump(f); // value
203 ++it;
204 }
205 }
206
207
get_data(archive_num & archive,const datetime & date,bool even_when_removed) const208 data_tree::lookup data_tree::get_data(archive_num & archive, const datetime & date, bool even_when_removed) const
209 {
210 map<archive_num, status>::const_iterator it = last_mod.begin();
211 datetime max_seen = datetime(0), max_real = datetime(0);
212 //archive (first argument) carries the last archive number (in the order of the database) in which a valid entry has been
213 //found, but which is not just a record "et_present" meaning that the file existed but has not been saved (differential backup context) or "et_absent" (no entry in the corresponding archive).
214 archive_num archive_seen = 0; //< last archive number (in the order of the database) in which a valid entry has been found (any state)
215 archive_num archive_even_when_removed = 0; //< last archive number in which a valid entry with data available has been found
216 bool presence_seen = false; //< whether the last found valid entry indicates file was present or removed
217 bool presence_real = false; //< whether the last found valid entry not being in an "et_present" state was present or removed
218 lookup ret;
219
220 archive = 0; // 0 is never assigned to an archive number
221 while(it != last_mod.end())
222 {
223 if(it->second.date >= max_seen // > and = because there should never be twice the same value
224 // and we must be able to see a value of 0 (initially max = 0) which is valid.
225 && (date.is_null() || it->second.date <= date))
226 {
227 max_seen = it->second.date;
228 archive_seen = it->first;
229 switch(it->second.present)
230 {
231 case et_saved:
232 case et_present:
233 presence_seen = true;
234 break;
235 case et_removed:
236 case et_absent:
237 presence_seen = false;
238 break;
239 default:
240 throw SRC_BUG;
241 }
242 }
243
244 if(it->second.date >= max_real // > and = because there should never be twice the same value
245 // and we must be able to see a value of 0 (initially max = 0) which is valid.
246 && (date.is_null() || it->second.date <= date))
247 {
248 if(it->second.present != et_present)
249 {
250 max_real = it->second.date;
251 archive = it->first;
252 switch(it->second.present)
253 {
254 case et_saved:
255 presence_real = true;
256 archive_even_when_removed = archive;
257 break;
258 case et_removed:
259 case et_absent:
260 presence_real = false;
261 break;
262 case et_present:
263 throw SRC_BUG;
264 default:
265 throw SRC_BUG;
266 }
267 }
268 }
269
270 ++it;
271 }
272
273 if(even_when_removed && archive_even_when_removed > 0)
274 {
275 archive = archive_even_when_removed;
276 presence_seen = presence_real = true;
277 }
278
279 if(archive == 0)
280 if(archive_seen != 0) // last acceptable entry is a file present but not saved, but no full backup is present in a previous archive
281 {
282 archive = archive_seen;
283 ret = not_restorable;
284 }
285 else
286 ret = not_found;
287 else
288 if(archive_seen != 0)
289 if(presence_seen && !presence_real) // last acceptable entry is a file present but not saved,
290 // but no full backup is present in a previous archive
291 {
292 archive = archive_seen;
293 ret = not_restorable;
294 }
295 else
296 if(presence_seen != presence_real)
297 throw SRC_BUG;
298 else // archive > 0 && presence_seen == present_real
299 if(presence_real)
300 ret = found_present;
301 else
302 ret = found_removed;
303 else // archive != 0 but archive_seen == 0
304 throw SRC_BUG;
305
306
307 return ret;
308 }
309
get_EA(archive_num & archive,const datetime & date,bool even_when_removed) const310 data_tree::lookup data_tree::get_EA(archive_num & archive, const datetime & date, bool even_when_removed) const
311 {
312 map<archive_num, status>::const_iterator it = last_change.begin();
313 datetime max_seen = datetime(0), max_real = datetime(0);
314 bool presence_seen = false, presence_real = false;
315 archive_num archive_seen = 0;
316 archive_num archive_even_when_removed = 0;
317 lookup ret;
318
319 archive = 0; // 0 is never assigned to an archive number
320 while(it != last_change.end())
321 {
322 if(it->second.date >= max_seen // > and = because there should never be twice the same value
323 // and we must be able to see a value of 0 (initially max = 0) which is valid.
324 && (date.is_null() || it->second.date <= date))
325 {
326 max_seen = it->second.date;
327 archive_seen = it->first;
328 switch(it->second.present)
329 {
330 case et_saved:
331 case et_present:
332 presence_seen = true;
333 break;
334 case et_removed:
335 case et_absent:
336 presence_seen = false;
337 break;
338 default:
339 throw SRC_BUG;
340 }
341 }
342
343 if(it->second.date >= max_real // > and = because there should never be twice the same value
344 // and we must be able to see a value of 0 (initially max = 0) which is valid.
345 && (date.is_null() || it->second.date <= date))
346 {
347 if(it->second.present != et_present)
348 {
349 max_real = it->second.date;
350 archive = it->first;
351 switch(it->second.present)
352 {
353 case et_saved:
354 presence_real = true;
355 archive_even_when_removed = archive;
356 break;
357 case et_removed:
358 case et_absent:
359 presence_real = false;
360 break;
361 case et_present:
362 throw SRC_BUG;
363 default:
364 throw SRC_BUG;
365 }
366 }
367 }
368
369 ++it;
370 }
371
372 if(even_when_removed && archive_even_when_removed > 0)
373 {
374 archive = archive_even_when_removed;
375 presence_seen = presence_real = true;
376 }
377
378
379 if(archive == 0)
380 if(archive_seen != 0) // last acceptable entry is a file present but not saved, but no full backup is present in a previous archive
381 ret = not_restorable;
382 else
383 ret = not_found;
384 else
385 if(archive_seen != 0)
386 if(presence_seen && !presence_real) // last acceptable entry is a file present but not saved, but no full backup is present in a previous archive
387 ret = not_restorable;
388 else
389 if(presence_seen != presence_real)
390 throw SRC_BUG;
391 else // archive > 0 && presence_seen == present_real
392 if(presence_real)
393 ret = found_present;
394 else
395 ret = found_removed;
396 else
397 throw SRC_BUG;
398
399 return ret;
400 }
401
read_data(archive_num num,datetime & val,etat & present) const402 bool data_tree::read_data(archive_num num, datetime & val, etat & present) const
403 {
404 map<archive_num, status>::const_iterator it = last_mod.find(num);
405
406 if(it != last_mod.end())
407 {
408 val = it->second.date;
409 present = it->second.present;
410 return true;
411 }
412 else
413 return false;
414 }
415
read_EA(archive_num num,datetime & val,etat & present) const416 bool data_tree::read_EA(archive_num num, datetime & val, etat & present) const
417 {
418 map<archive_num, status>::const_iterator it = last_change.find(num);
419
420 if(it != last_change.end())
421 {
422 val = it->second.date;
423 present = it->second.present;
424 return true;
425 }
426 else
427 return false;
428 }
429
finalize(const archive_num & archive,const datetime & deleted_date,const archive_num & ignore_archives_greater_or_equal)430 void data_tree::finalize(const archive_num & archive,
431 const datetime & deleted_date,
432 const archive_num & ignore_archives_greater_or_equal)
433 {
434 map<archive_num, status>::iterator it = last_mod.begin();
435 bool presence_max = false;
436 archive_num num_max = 0;
437 datetime last_mtime = datetime(0); // used as deleted_date for EA if last mtime is found
438 bool found_in_archive = false;
439
440 // checking the last_mod map
441
442 while(it != last_mod.end() && !found_in_archive)
443 {
444 if(it->first == archive && it->second.present != et_absent)
445 found_in_archive = true;
446 else
447 if(it->first > num_max && (it->first < ignore_archives_greater_or_equal || ignore_archives_greater_or_equal == 0))
448 {
449 num_max = it->first;
450 switch(it->second.present)
451 {
452 case et_saved:
453 case et_present:
454 presence_max = true;
455 last_mtime = it->second.date; // used as deleted_data for EA
456 break;
457 case et_removed:
458 case et_absent:
459 presence_max = false;
460 last_mtime = it->second.date; // keeping this date as it is
461 // not possible to know when the EA have been removed
462 // since it does not change mtime of file nor of its parent
463 // directory (this is an heuristic).
464 break;
465 default:
466 throw SRC_BUG;
467 }
468 }
469 ++it;
470 }
471
472 if(!found_in_archive) // not entry found for asked archive (or recorded as et_absent)
473 {
474 if(presence_max)
475 {
476 // the most recent entry found stated that this file
477 // existed at the time "last_mtime"
478 // and this entry is absent from the archive under addition
479 // thus we must record that it has been removed in the
480 // archive we currently add.
481
482 if(deleted_date > last_mtime)
483 set_data(archive, deleted_date, et_absent);
484 // add an entry telling that this file does no more exist in the current archive
485 else
486 set_data(archive, last_mtime, et_absent);
487 // add an entry telling thatthis file does no more exists, using the last known date
488 // as deleted data. This situation may appear when one makes a first backup
489 // then a second one but excluding from the backup that particular file. This file
490 // may reappear later with if is backup included in the backup possibily with the same
491 // date. In 2.4.x we added 1 second to the last known date to create the deleted date
492 // which lead out of order warning to show for the database. Since 2.5.x date resolution
493 // may be one microsecond (no more 1 second) thus we now keep the last known date as
494 // delete date
495 }
496 else // the entry has been seen previously but as removed in the latest state,
497 // if we already have an et_absent entry (which is possible during a reordering archive operation within a database), we can (and must) suppress it.
498 {
499 map<archive_num, status>::iterator it = last_mod.find(archive);
500 if(it != last_mod.end()) // we have an entry associated to this archive
501 {
502 switch(it->second.present)
503 {
504 case et_saved:
505 case et_present:
506 throw SRC_BUG; // entry has not been found in the current archive
507 case et_removed:
508 break; // we must keep it, it was in the original archive
509 case et_absent:
510 last_mod.erase(it); // this entry had been added from previous neighbor archive, we must remove it now, it was not part of the original archive
511 break;
512 default:
513 throw SRC_BUG;
514 }
515 }
516 // else already absent from the base for this archive, cool.
517 }
518 }
519 // else, entry found for the current archive
520
521
522
523 //////////////////////
524 // now checking the last_change map
525 //
526
527
528 it = last_change.begin();
529 presence_max = false;
530 num_max = 0;
531 found_in_archive = false;
532
533 while(it != last_change.end() && !found_in_archive)
534 {
535 if(it->first == archive && it->second.present != et_absent)
536 found_in_archive = true;
537 else
538 if(it->first > num_max && (it->first < ignore_archives_greater_or_equal || ignore_archives_greater_or_equal == 0))
539 {
540 num_max = it->first;
541 switch(it->second.present)
542 {
543 case et_saved:
544 case et_present:
545 presence_max = true;
546 break;
547 case et_removed:
548 case et_absent:
549 presence_max = false;
550 break;
551 default:
552 throw SRC_BUG;
553 }
554 }
555 ++it;
556 }
557
558 if(!found_in_archive) // not entry found for asked archive
559 {
560 if(num_max != 0) // num_max may be equal to zero if this entry never had any EA in any recorded archive
561 if(presence_max)
562 set_EA(archive, (last_mtime < deleted_date ? deleted_date : last_mtime), et_absent); // add an entry telling that EA for this file do no more exist in the current archive
563 // else last entry found stated the EA was removed, nothing more to do
564 }
565 // else, entry found for the current archive
566 }
567
remove_all_from(const archive_num & archive_to_remove,const archive_num & last_archive)568 bool data_tree::remove_all_from(const archive_num & archive_to_remove, const archive_num & last_archive)
569 {
570 map<archive_num, status>::iterator it = last_mod.begin();
571
572
573 // if this file was stored as "removed" in the archive we tend to remove from the database
574 // this "removed" information is propagated to the next archive if both:
575 // - the next archive exists and has no information recorded about this file
576 // - this entry does not only exist in the archive about to be removed
577 if(archive_to_remove < last_archive)
578 {
579 datetime del_date;
580 etat status;
581 if(last_mod.size() > 1 && read_data(archive_to_remove, del_date, status))
582 if(status == et_removed)
583 {
584 datetime tmp;
585 if(!read_data(archive_to_remove + 1, tmp, status))
586 set_data(archive_to_remove + 1, del_date, et_removed);
587 }
588 if(last_change.size() > 1 && read_EA(archive_to_remove, del_date, status))
589 if(status == et_removed)
590 {
591 datetime tmp;
592 if(!read_EA(archive_to_remove + 1, tmp, status))
593 set_EA(archive_to_remove + 1, del_date, et_removed);
594 }
595 }
596
597 while(it != last_mod.end())
598 {
599 if(it->first == archive_to_remove)
600 {
601 last_mod.erase(it);
602 break; // stops the while loop, as there is at most one element with that key
603 }
604 else
605 ++it;
606 }
607
608 it = last_change.begin();
609 while(it != last_change.end())
610 {
611 if(it->first == archive_to_remove)
612 {
613 last_change.erase(it);
614 break; // stops the while loop, as there is at most one element with that key
615 }
616 else
617 ++it;
618 }
619
620 return last_mod.empty() && last_change.empty();
621 }
622
listing(user_interaction & dialog) const623 void data_tree::listing(user_interaction & dialog) const
624 {
625 map<archive_num, status>::const_iterator it = last_mod.begin();
626 map<archive_num, status>::const_iterator ut = last_change.begin();
627
628 dialog.printf(gettext("Archive number | Data | status || EA | status \n"));
629 dialog.printf(gettext("---------------+-------------------------+--------++-------------------------+----------\n"));
630
631 while(it != last_mod.end() || ut != last_change.end())
632 {
633 if(it != last_mod.end())
634 if(ut != last_change.end())
635 if(it->first == ut->first)
636 {
637 display_line(dialog, it->first, &(it->second.date), it->second.present, &(ut->second.date), ut->second.present);
638 ++it;
639 ++ut;
640 }
641 else // not in the same archive
642 if(it->first < ut->first) // it only
643 {
644 display_line(dialog, it->first, &(it->second.date), it->second.present, nullptr, data_tree::et_removed);
645 ++it;
646 }
647 else // ut only
648 {
649 display_line(dialog, ut->first, nullptr, data_tree::et_removed, &(ut->second.date), ut->second.present);
650 ++ut;
651 }
652 else // ut at end of list thus it != last_mod.end() (see while condition)
653 {
654 display_line(dialog, it->first, &(it->second.date), it->second.present, nullptr, data_tree::et_removed);
655 ++it;
656 }
657 else // it at end of list, this ut != last_change.end() (see while condition)
658 {
659 display_line(dialog, ut->first, nullptr, data_tree::et_removed, &(ut->second.date), ut->second.present);
660 ++ut;
661 }
662 }
663 }
664
apply_permutation(archive_num src,archive_num dst)665 void data_tree::apply_permutation(archive_num src, archive_num dst)
666 {
667 map<archive_num, status> transfert;
668 map<archive_num, status>::iterator it = last_mod.begin();
669
670 transfert.clear();
671 while(it != last_mod.end())
672 {
673 transfert[data_tree_permutation(src, dst, it->first)] = it->second;
674 ++it;
675 }
676 last_mod = transfert;
677
678 transfert.clear();
679 it = last_change.begin();
680 while(it != last_change.end())
681 {
682 transfert[data_tree_permutation(src, dst, it->first)] = it->second;
683 ++it;
684 }
685 last_change = transfert;
686 }
687
skip_out(archive_num num)688 void data_tree::skip_out(archive_num num)
689 {
690 map<archive_num, status> resultant;
691 map<archive_num, status>::iterator it = last_mod.begin();
692 infinint tmp;
693
694 while(it != last_mod.end())
695 {
696 if(it->first > num)
697 resultant[it->first-1] = it->second;
698 else
699 resultant[it->first] = it->second;
700 ++it;
701 }
702 last_mod = resultant;
703
704 resultant.clear();
705 it = last_change.begin();
706 while(it != last_change.end())
707 {
708 if(it->first > num)
709 resultant[it->first-1] = it->second;
710 else
711 resultant[it->first] = it->second;
712 ++it;
713 }
714 last_change = resultant;
715 }
716
compute_most_recent_stats(vector<infinint> & data,vector<infinint> & ea,vector<infinint> & total_data,vector<infinint> & total_ea) const717 void data_tree::compute_most_recent_stats(vector<infinint> & data, vector<infinint> & ea,
718 vector<infinint> & total_data, vector<infinint> & total_ea) const
719 {
720 archive_num most_recent = 0;
721 datetime max = datetime(0);
722 map<archive_num, status>::const_iterator it = last_mod.begin();
723
724 while(it != last_mod.end())
725 {
726 if(it->second.present == et_saved)
727 {
728 if(it->second.date >= max)
729 {
730 most_recent = it->first;
731 max = it->second.date;
732 }
733 ++total_data[it->first];
734 }
735 ++it;
736 }
737 if(most_recent > 0)
738 ++data[most_recent];
739
740 most_recent = 0;
741 max = datetime(0);
742 it = last_change.begin();
743
744 while(it != last_change.end())
745 {
746 if(it->second.present == et_saved)
747 {
748 if(it->second.date >= max)
749 {
750 most_recent = it->first;
751 max = it->second.date;
752 }
753 ++total_ea[it->first];
754 }
755 ++it;
756 }
757 if(most_recent > 0)
758 ++ea[most_recent];
759 }
760
761 // helper data structure for check_map_order method
762
763 struct trecord
764 {
765 datetime date;
766 bool set;
767
trecordlibdar::trecord768 trecord() { set = false; date = datetime(0); };
trecordlibdar::trecord769 trecord(const trecord & ref) { date = ref.date; set = ref.set; };
operator =libdar::trecord770 const trecord & operator = (const trecord & ref) { date = ref.date; set = ref.set; return *this; };
771 };
772
773
fix_corruption()774 bool data_tree::fix_corruption()
775 {
776 bool ret = true;
777 map<archive_num, status>::iterator it = last_mod.begin();
778
779 while(it != last_mod.end() && ret)
780 {
781 if(it->second.present != et_removed && it->second.present != et_absent)
782 ret = false;
783 ++it;
784 }
785
786 it = last_change.begin();
787 while(it != last_change.end() && ret)
788 {
789 if(it->second.present != et_removed && it->second.present != et_absent)
790 ret = false;
791 ++it;
792 }
793
794 return ret;
795 }
796
check_map_order(user_interaction & dialog,const map<archive_num,status> the_map,const path & current_path,const string & field_nature,bool & initial_warn) const797 bool data_tree::check_map_order(user_interaction & dialog,
798 const map<archive_num, status> the_map,
799 const path & current_path,
800 const string & field_nature,
801 bool & initial_warn) const
802 {
803
804
805 // some variable to work with around the_map
806
807 U_I dates_size = the_map.size()+1;
808 vector<trecord> dates = vector<trecord>(dates_size);
809 map<archive_num, status>::const_iterator it = the_map.begin();
810 vector<trecord>::iterator rec_it;
811 datetime last_date = datetime(0);
812
813 // now the algorithm
814
815 // extracting dates from map into an ordered vector
816
817 while(it != the_map.end())
818 {
819 trecord rec;
820
821 rec.date = it->second.date;
822 rec.set = true;
823 while(dates_size <= it->first)
824 {
825 dates.push_back(trecord());
826 ++dates_size;
827 }
828 dates[it->first] = rec;
829
830 ++it;
831 }
832
833 // checking whether dates are sorted crescendo following the archive number
834
835 rec_it = dates.begin();
836
837 while(rec_it != dates.end())
838 {
839 if(rec_it->set)
840 {
841 if(rec_it->date >= last_date)
842 last_date = rec_it->date;
843 else // order is not respected
844 {
845 string tmp = current_path.display() == "." ? get_name() : (current_path + get_name()).display();
846 dialog.printf(gettext("Dates of file's %S are not increasing when database's archive number grows. Concerned file is: %S"), &field_nature, &tmp);
847 if(initial_warn)
848 {
849 dialog.warning(gettext("Dates are not increasing for all files when database's archive number grows, working with this database may lead to improper file's restored version. Please reorder the archive within the database in the way that the older is the first archive and so on up to the most recent archive being the last of the database"));
850 try
851 {
852 dialog.pause(gettext("Do you want to ignore the same type of error for other files?"));
853 return false;
854 }
855 catch(Euser_abort & e)
856 {
857 initial_warn = false;
858 }
859 }
860 break; // aborting the while loop
861 }
862 }
863 ++rec_it;
864 }
865
866 return true;
867 }
868
869
870 ////////////////////////////////////////////////////////////////
871
data_dir(const string & name)872 data_dir::data_dir(const string &name) : data_tree(name)
873 {
874 rejetons.clear();
875 }
876
data_dir(generic_file & f,unsigned char db_version)877 data_dir::data_dir(generic_file &f, unsigned char db_version) : data_tree(f, db_version)
878 {
879 infinint tmp = infinint(f); // number of children
880 data_tree *entry = nullptr;
881 rejetons.clear();
882
883 try
884 {
885 while(!tmp.is_zero())
886 {
887 entry = read_from_file(f, db_version, get_pool());
888 if(entry == nullptr)
889 throw Erange("data_dir::data_dir", gettext("Unexpected end of file"));
890 rejetons.push_back(entry);
891 entry = nullptr;
892 --tmp;
893 }
894 }
895 catch(...)
896 {
897 list<data_tree *>::iterator next = rejetons.begin();
898 while(next != rejetons.end())
899 {
900 delete *next;
901 *next = nullptr;
902 ++next;
903 }
904 if(entry != nullptr)
905 delete entry;
906 throw;
907 }
908 }
909
data_dir(const data_dir & ref)910 data_dir::data_dir(const data_dir & ref) : data_tree(ref)
911 {
912 rejetons.clear();
913 }
914
data_dir(const data_tree & ref)915 data_dir::data_dir(const data_tree & ref) : data_tree(ref)
916 {
917 rejetons.clear();
918 }
919
~data_dir()920 data_dir::~data_dir()
921 {
922 list<data_tree *>::iterator next = rejetons.begin();
923 while(next != rejetons.end())
924 {
925 delete *next;
926 *next = nullptr;
927 ++next;
928 }
929 }
930
dump(generic_file & f) const931 void data_dir::dump(generic_file & f) const
932 {
933 list<data_tree *>::const_iterator it = rejetons.begin();
934 infinint tmp = rejetons.size();
935
936 data_tree::dump(f);
937 tmp.dump(f);
938 while(it != rejetons.end())
939 {
940 if(*it == nullptr)
941 throw SRC_BUG;
942 (*it)->dump(f);
943 ++it;
944 }
945 }
946
947
find_or_addition(const string & name,bool is_dir,const archive_num & archive)948 data_tree *data_dir::find_or_addition(const string & name, bool is_dir, const archive_num & archive)
949 {
950 const data_tree *fils = read_child(name);
951 data_tree *ret = nullptr;
952
953 if(fils == nullptr) // brand-new data_tree to build
954 {
955 if(is_dir)
956 ret = new (get_pool()) data_dir(name);
957 else
958 ret = new (get_pool()) data_tree(name);
959 if(ret == nullptr)
960 throw Ememory("data_dir::find_or_addition");
961 add_child(ret);
962 }
963 else // already saved in another archive
964 {
965 // check if dir/file nature has changed
966 const data_dir *fils_dir = dynamic_cast<const data_dir *>(fils);
967 if(fils_dir == nullptr && is_dir) // need to upgrade data_tree to data_dir
968 {
969 ret = new (get_pool()) data_dir(*fils); // upgrade data_tree in an empty data_dir
970 if(ret == nullptr)
971 throw Ememory("data_dir::find_or_addition");
972 try
973 {
974 remove_child(name);
975 add_child(ret);
976 }
977 catch(...)
978 {
979 delete ret;
980 throw;
981 }
982 }
983 else // no change in dir/file nature
984 ret = const_cast<data_tree *>(fils);
985 }
986
987 return ret;
988 }
989
add(const cat_inode * entry,const archive_num & archive)990 void data_dir::add(const cat_inode *entry, const archive_num & archive)
991 {
992 const cat_directory *entry_dir = dynamic_cast<const cat_directory *>(entry);
993 data_tree * tree = find_or_addition(entry->get_name(), entry_dir != nullptr, archive);
994 archive_num last_archive;
995 lookup result;
996 datetime last_mod = entry->get_last_modif() > entry->get_last_change() ? entry->get_last_modif() : entry->get_last_change();
997
998 switch(entry->get_saved_status())
999 {
1000 case s_saved:
1001 case s_fake:
1002 tree->set_data(archive, last_mod, et_saved);
1003 break;
1004 case s_not_saved:
1005 tree->set_data(archive, last_mod, et_present);
1006 break;
1007 default:
1008 throw SRC_BUG;
1009 }
1010
1011 switch(entry->ea_get_saved_status())
1012 {
1013 case cat_inode::ea_none:
1014 break;
1015 case cat_inode::ea_removed:
1016 result = tree->get_EA(last_archive, datetime(0), false);
1017 if(result == found_present || result == not_restorable)
1018 tree->set_EA(archive, entry->get_last_change(), et_removed);
1019 // else no need to add an et_remove entry in the map
1020 break;
1021 case cat_inode::ea_partial:
1022 tree->set_EA(archive, entry->get_last_change(), et_present);
1023 break;
1024 case cat_inode::ea_fake:
1025 case cat_inode::ea_full:
1026 tree->set_EA(archive, entry->get_last_change(), et_saved);
1027 break;
1028 default:
1029 throw SRC_BUG;
1030 }
1031 }
1032
add(const cat_detruit * entry,const archive_num & archive)1033 void data_dir::add(const cat_detruit *entry, const archive_num & archive)
1034 {
1035 data_tree * tree = find_or_addition(entry->get_name(), false, archive);
1036 archive_num last_archive;
1037 lookup result;
1038
1039 result = tree->get_data(last_archive, datetime(0), false);
1040 if(result == found_present || result == not_restorable)
1041 tree->set_data(archive, entry->get_date(), et_removed);
1042 result = tree->get_EA(last_archive, datetime(0), false);
1043 if(result == found_present || result == not_restorable)
1044 tree->set_EA(archive, entry->get_date(), et_removed);
1045 }
1046
read_child(const string & name) const1047 const data_tree *data_dir::read_child(const string & name) const
1048 {
1049 list<data_tree *>::const_iterator it = rejetons.begin();
1050
1051 while(it != rejetons.end() && *it != nullptr && (*it)->get_name() != name)
1052 ++it;
1053
1054 if(it == rejetons.end())
1055 return nullptr;
1056 else
1057 if(*it == nullptr)
1058 throw SRC_BUG;
1059 else
1060 return *it;
1061 }
1062
read_all_children(vector<string> & fils) const1063 void data_dir::read_all_children(vector<string> & fils) const
1064 {
1065 list<data_tree *>::const_iterator it = rejetons.begin();
1066
1067 fils.clear();
1068 while(it != rejetons.end())
1069 fils.push_back((*it++)->get_name());
1070 }
1071
check_order(user_interaction & dialog,const path & current_path,bool & initial_warn) const1072 bool data_dir::check_order(user_interaction & dialog, const path & current_path, bool & initial_warn) const
1073 {
1074 list<data_tree *>::const_iterator it = rejetons.begin();
1075 bool ret = data_tree::check_order(dialog, current_path, initial_warn);
1076 path subpath = current_path.display() == "." ? get_name() : current_path + get_name();
1077
1078 while(it != rejetons.end() && ret)
1079 {
1080 if(*it == nullptr)
1081 throw SRC_BUG;
1082 ret = (*it)->check_order(dialog, subpath, initial_warn);
1083 ++it;
1084 }
1085
1086 return ret;
1087 }
1088
finalize(const archive_num & archive,const datetime & deleted_date,const archive_num & ignore_archives_greater_or_equal)1089 void data_dir::finalize(const archive_num & archive, const datetime & deleted_date, const archive_num & ignore_archives_greater_or_equal)
1090 {
1091 datetime new_deleted_date;
1092 archive_num tmp_archive;
1093 etat tmp_presence;
1094
1095 data_tree::finalize(archive, deleted_date, ignore_archives_greater_or_equal);
1096
1097 switch(get_data(tmp_archive, datetime(0), false))
1098 {
1099 case found_present:
1100 case found_removed:
1101 break; // acceptable result
1102 case not_found:
1103 if(fix_corruption())
1104 throw Edata("This is to signal the caller of this method that this object has to be removed from database"); // exception caugth in data_dir::finalize_except_self
1105 throw Erange("data_dir::finalize", gettext("This database has been corrupted probably due to a bug in release 2.4.0 to 2.4.9, and it has not been possible to cleanup this corruption, please rebuild the database from archives or extracted \"catalogues\", if the database has never been used by one of the previously mentioned released, you are welcome to open a bug report and provide as much as possible details about the circumstances"));
1106 case not_restorable:
1107 break; // also an acceptable result;
1108 default:
1109 throw SRC_BUG;
1110 }
1111
1112 if(!read_data(tmp_archive, new_deleted_date, tmp_presence))
1113 throw SRC_BUG;
1114
1115 finalize_except_self(archive, new_deleted_date, ignore_archives_greater_or_equal);
1116 }
1117
finalize_except_self(const archive_num & archive,const datetime & deleted_date,const archive_num & ignore_archives_greater_or_equal)1118 void data_dir::finalize_except_self(const archive_num & archive, const datetime & deleted_date, const archive_num & ignore_archives_greater_or_equal)
1119 {
1120 list<data_tree *>::iterator it = rejetons.begin();
1121
1122 while(it != rejetons.end())
1123 {
1124 if(*it == nullptr)
1125 throw SRC_BUG;
1126 try
1127 {
1128 (*it)->finalize(archive, deleted_date, ignore_archives_greater_or_equal);
1129 ++it;
1130 }
1131 catch(Edata & e)
1132 {
1133 delete (*it);
1134 rejetons.erase(it);
1135 it = rejetons.begin();
1136 }
1137 }
1138 }
1139
1140
remove_all_from(const archive_num & archive_to_remove,const archive_num & last_archive)1141 bool data_dir::remove_all_from(const archive_num & archive_to_remove, const archive_num & last_archive)
1142 {
1143 list<data_tree *>::iterator it = rejetons.begin();
1144
1145 while(it != rejetons.end())
1146 {
1147 if((*it) == nullptr)
1148 throw SRC_BUG;
1149 if((*it)->remove_all_from(archive_to_remove, last_archive))
1150 {
1151 delete *it; // release the memory used by the object
1152 *it = nullptr;
1153 rejetons.erase(it); // remove the entry from the list
1154 it = rejetons.begin(); // does not seems "it" points to the next item after erase, so we restart from the beginning
1155 }
1156 else
1157 ++it;
1158 }
1159
1160 return data_tree::remove_all_from(archive_to_remove, last_archive) && rejetons.size() == 0;
1161 }
1162
show(user_interaction & dialog,archive_num num,string marge) const1163 void data_dir::show(user_interaction & dialog, archive_num num, string marge) const
1164 {
1165 list<data_tree *>::const_iterator it = rejetons.begin();
1166 archive_num ou_data, ou_ea;
1167 bool data, ea;
1168 string etat, name;
1169 lookup lo_data, lo_ea;
1170 bool even_when_removed = (num != 0);
1171
1172 while(it != rejetons.end())
1173 {
1174 if(*it == nullptr)
1175 throw SRC_BUG;
1176 data_dir *dir = dynamic_cast<data_dir *>(*it);
1177
1178 lo_data = (*it)->get_data(ou_data, datetime(0), even_when_removed);
1179 lo_ea = (*it)->get_EA(ou_ea, datetime(0), even_when_removed);
1180 data = lo_data == found_present && (ou_data == num || num == 0);
1181 ea = lo_ea == found_present && (ou_ea == num || num == 0);
1182 name = (*it)->get_name();
1183 if(data || ea || num == 0)
1184 {
1185 etat = "";
1186 if(data)
1187 etat += gettext("[ Saved ]");
1188 else
1189 etat += gettext("[ ]");
1190
1191 if(ea)
1192 etat += gettext("[ EA ]");
1193 else
1194 etat += gettext("[ ]");
1195
1196 if(dialog.get_use_dar_manager_show_files())
1197 dialog.dar_manager_show_files(name, data, ea);
1198 else
1199 dialog.printf("%S %S%S\n", &etat, &marge, &name);
1200 }
1201 if(dir != nullptr)
1202 dir->show(dialog, num, marge+name+"/");
1203 ++it;
1204 }
1205 }
1206
apply_permutation(archive_num src,archive_num dst)1207 void data_dir::apply_permutation(archive_num src, archive_num dst)
1208 {
1209 list<data_tree *>::iterator it = rejetons.begin();
1210
1211 data_tree::apply_permutation(src, dst);
1212 while(it != rejetons.end())
1213 {
1214 (*it)->apply_permutation(src, dst);
1215 ++it;
1216 }
1217 }
1218
1219
skip_out(archive_num num)1220 void data_dir::skip_out(archive_num num)
1221 {
1222 list<data_tree *>::iterator it = rejetons.begin();
1223
1224 data_tree::skip_out(num);
1225 while(it != rejetons.end())
1226 {
1227 (*it)->skip_out(num);
1228 ++it;
1229 }
1230 }
1231
compute_most_recent_stats(vector<infinint> & data,vector<infinint> & ea,vector<infinint> & total_data,vector<infinint> & total_ea) const1232 void data_dir::compute_most_recent_stats(vector<infinint> & data, vector<infinint> & ea,
1233 vector<infinint> & total_data, vector<infinint> & total_ea) const
1234 {
1235 list<data_tree *>::const_iterator it = rejetons.begin();
1236
1237 data_tree::compute_most_recent_stats(data, ea, total_data, total_ea);
1238 while(it != rejetons.end())
1239 {
1240 (*it)->compute_most_recent_stats(data, ea, total_data, total_ea);
1241 ++it;
1242 }
1243 }
1244
fix_corruption()1245 bool data_dir::fix_corruption()
1246 {
1247 while(rejetons.begin() != rejetons.end() && *(rejetons.begin()) != nullptr && (*(rejetons.begin()))->fix_corruption())
1248 {
1249 delete *(rejetons.begin());
1250 rejetons.erase(rejetons.begin());
1251 }
1252
1253 if(rejetons.begin() != rejetons.end())
1254 return false;
1255 else
1256 return data_tree::fix_corruption();
1257
1258 }
1259
add_child(data_tree * fils)1260 void data_dir::add_child(data_tree *fils)
1261 {
1262 if(fils == nullptr)
1263 throw SRC_BUG;
1264 rejetons.push_back(fils);
1265 }
1266
remove_child(const string & name)1267 void data_dir::remove_child(const string & name)
1268 {
1269 list<data_tree *>::iterator it = rejetons.begin();
1270
1271 while(it != rejetons.end() && *it != nullptr && (*it)->get_name() != name)
1272 ++it;
1273
1274 if(it != rejetons.end())
1275 {
1276 if(*it == nullptr)
1277 throw SRC_BUG;
1278 else
1279 rejetons.erase(it);
1280 }
1281 }
1282
1283
1284 ////////////////////////////////////////////////////////////////
1285
data_tree_read(generic_file & f,unsigned char db_version,memory_pool * pool)1286 data_dir *data_tree_read(generic_file & f, unsigned char db_version, memory_pool *pool)
1287 {
1288 data_tree *lu = read_from_file(f, db_version, pool);
1289 data_dir *ret = dynamic_cast<data_dir *>(lu);
1290
1291 if(ret == nullptr && lu != nullptr)
1292 delete lu;
1293
1294 return ret;
1295 }
1296
data_tree_find(path chemin,const data_dir & racine,const data_tree * & ptr)1297 bool data_tree_find(path chemin, const data_dir & racine, const data_tree *& ptr)
1298 {
1299 string filename;
1300 const data_dir *current = &racine;
1301 bool loop = true;
1302
1303 if(!chemin.is_relative())
1304 throw SRC_BUG;
1305
1306 while(loop)
1307 {
1308 if(!chemin.pop_front(filename))
1309 {
1310 filename = chemin.display();
1311 loop = false;
1312 }
1313 ptr = current->read_child(filename);
1314 if(ptr == nullptr)
1315 loop = false;
1316 if(loop)
1317 {
1318 current = dynamic_cast<const data_dir *>(ptr);
1319 if(current == nullptr)
1320 {
1321 loop = false;
1322 ptr = nullptr;
1323 }
1324 }
1325 }
1326
1327 return ptr != nullptr;
1328 }
1329
data_tree_update_with(const cat_directory * dir,archive_num archive,data_dir * racine)1330 void data_tree_update_with(const cat_directory *dir, archive_num archive, data_dir *racine)
1331 {
1332 const cat_nomme *entry;
1333
1334 dir->reset_read_children();
1335 while(dir->read_children(entry))
1336 {
1337 const cat_directory *entry_dir = dynamic_cast<const cat_directory *>(entry);
1338 const cat_inode *entry_ino = dynamic_cast<const cat_inode *>(entry);
1339 const cat_mirage *entry_mir = dynamic_cast<const cat_mirage *>(entry);
1340 const cat_detruit *entry_det = dynamic_cast<const cat_detruit *>(entry);
1341
1342 if(entry_mir != nullptr)
1343 {
1344 entry_ino = entry_mir->get_inode();
1345 entry_mir->get_inode()->change_name(entry_mir->get_name());
1346 }
1347
1348 if(entry_ino == nullptr)
1349 if(entry_det != nullptr)
1350 {
1351 if(!entry_det->get_date().is_null())
1352 racine->add(entry_det, archive);
1353 // else this is an old archive that does not store date with cat_detruit objects
1354 }
1355 else
1356 continue; // continue with next loop, we ignore entree objects that are neither inode nor cat_detruit
1357 else
1358 racine->add(entry_ino, archive);
1359
1360 if(entry_dir != nullptr) // going into recursion
1361 {
1362 data_tree *new_root = const_cast<data_tree *>(racine->read_child(entry->get_name()));
1363 data_dir *new_root_dir = dynamic_cast<data_dir *>(new_root);
1364
1365 if(new_root == nullptr)
1366 throw SRC_BUG; // the racine->add method did not add an item for "entry"
1367 if(new_root_dir == nullptr)
1368 throw SRC_BUG; // the racine->add method did not add a data_dir item
1369 data_tree_update_with(entry_dir, archive, new_root_dir);
1370 }
1371 }
1372 }
1373
data_tree_permutation(archive_num src,archive_num dst,archive_num x)1374 archive_num data_tree_permutation(archive_num src, archive_num dst, archive_num x)
1375 {
1376 if(src < dst)
1377 if(x < src || x > dst)
1378 return x;
1379 else
1380 if(x == src)
1381 return dst;
1382 else
1383 return x-1;
1384 else
1385 if(src == dst)
1386 return x;
1387 else // src > dst
1388 if(x > src || x < dst)
1389 return x;
1390 else
1391 if(x == src)
1392 return dst;
1393 else
1394 return x+1;
1395 }
1396
1397 } // end of namespace
1398
1399
1400 ////////////////////////////////////////////////////////////////
1401
read_from_file(generic_file & f,unsigned char db_version,memory_pool * pool)1402 static data_tree *read_from_file(generic_file & f, unsigned char db_version, memory_pool *pool)
1403 {
1404 char sign;
1405 data_tree *ret;
1406
1407 if(f.read(&sign, 1) != 1)
1408 return nullptr; // nothing more to read
1409
1410 if(sign == data_tree::signature())
1411 ret = new (pool) data_tree(f, db_version);
1412 else if(sign == data_dir::signature())
1413 ret = new (pool) data_dir(f, db_version);
1414 else
1415 throw Erange("read_from_file", gettext("Unknown record type"));
1416
1417 if(ret == nullptr)
1418 throw Ememory("read_from_file");
1419
1420 return ret;
1421 }
1422
read_from_file(generic_file & f,archive_num & a)1423 static void read_from_file(generic_file &f, archive_num &a)
1424 {
1425 char buffer[sizeof(archive_num)];
1426 archive_num *ptr = (archive_num *)&(buffer[0]);
1427
1428 f.read(buffer, sizeof(archive_num));
1429 a = ntohs(*ptr);
1430 }
1431
write_to_file(generic_file & f,archive_num a)1432 static void write_to_file(generic_file &f, archive_num a)
1433 {
1434 char buffer[sizeof(archive_num)];
1435 archive_num *ptr = (archive_num *)&(buffer[0]);
1436
1437 *ptr = htons(a);
1438 f.write(buffer, sizeof(archive_num));
1439 }
1440
display_line(user_interaction & dialog,archive_num num,const datetime * data,data_tree::etat data_presence,const datetime * ea,data_tree::etat ea_presence)1441 static void display_line(user_interaction & dialog,
1442 archive_num num,
1443 const datetime *data,
1444 data_tree::etat data_presence,
1445 const datetime *ea,
1446 data_tree::etat ea_presence)
1447 {
1448
1449 const string REMOVED = gettext("removed ");
1450 const string PRESENT = gettext("present ");
1451 const string SAVED = gettext("saved ");
1452 const string ABSENT = gettext("absent ");
1453 const string NO_DATE = " ";
1454
1455 string data_state;
1456 string ea_state;
1457 string data_date;
1458 string ea_date;
1459
1460 switch(data_presence)
1461 {
1462 case data_tree::et_saved:
1463 data_state = SAVED;
1464 break;
1465 case data_tree::et_present:
1466 data_state = PRESENT;
1467 break;
1468 case data_tree::et_removed:
1469 data_state = REMOVED;
1470 break;
1471 case data_tree::et_absent:
1472 data_state = ABSENT;
1473 break;
1474 default:
1475 throw SRC_BUG;
1476 }
1477
1478 switch(ea_presence)
1479 {
1480 case data_tree::et_saved:
1481 ea_state = SAVED;
1482 break;
1483 case data_tree::et_present:
1484 ea_state = PRESENT;
1485 break;
1486 case data_tree::et_removed:
1487 ea_state = REMOVED;
1488 break;
1489 case data_tree::et_absent:
1490 throw SRC_BUG; // state not used for EA
1491 default:
1492 throw SRC_BUG;
1493 }
1494
1495 if(data == nullptr)
1496 {
1497 data_state = ABSENT;
1498 data_date = NO_DATE;
1499 }
1500 else
1501 data_date = tools_display_date(*data);
1502
1503 if(ea == nullptr)
1504 {
1505 ea_state = ABSENT;
1506 ea_date = NO_DATE;
1507 }
1508 else
1509 ea_date = tools_display_date(*ea);
1510
1511 if(dialog.get_use_dar_manager_show_version())
1512 dialog.dar_manager_show_version(num, data_date, data_state, ea_date, ea_state);
1513 else
1514 dialog.printf(" \t%u\t%S %S %S %S\n", num, &data_date, &data_state, &ea_date, &ea_state);
1515 }
1516