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_CTYPE_H 27 #include <ctype.h> 28 #endif 29 } // end extern "C" 30 31 #include <map> 32 #include "filtre.hpp" 33 #include "user_interaction.hpp" 34 #include "erreurs_ext.hpp" 35 #include "filesystem.hpp" 36 #include "ea.hpp" 37 #include "defile.hpp" 38 #include "null_file.hpp" 39 #include "thread_cancellation.hpp" 40 #include "compressor.hpp" 41 #include "sparse_file.hpp" 42 #include "semaphore.hpp" 43 #include "deci.hpp" 44 #include "cat_all_entrees.hpp" 45 46 using namespace std; 47 48 #define SKIPPED "Skipping file: " 49 #define SQUEEZED "Ignoring empty directory: " 50 51 namespace libdar 52 { 53 54 // returns false if file has changed during backup (inode is saved however, but the saved data may be invalid) 55 // return true if file has not change, false if file need not resaving or does not add wasted bytes in archive 56 // throw exceptions in case of error 57 static bool save_inode(user_interaction & dialog,//< how to report to user 58 memory_pool *pool, //< set to nullptr or points to the memory_pool to use 59 const string &info_quoi, //< full path name of the file to save (including its name) 60 cat_entree * & e, //< cat_entree to save to archive 61 const pile_descriptor & pdesc,//< where to write to 62 bool info_details, //< verbose output to user 63 bool display_treated, //< add an information line before treating a file 64 bool alter_atime, //< whether to set back atime of filesystem 65 bool check_change, //< whether to check file change during backup 66 bool compute_crc, //< whether to recompute the CRC 67 cat_file::get_data_mode keep_mode, //< whether to copy compressed data (same compression algo), uncompress but keep hole structure (change compression algo) or uncompress and fill data holes (redetect holes in file) 68 const catalogue & cat, //< catalogue to update for escape sequence mark 69 const infinint & repeat_count, //< how much time to retry saving the file if it has changed during the backup 70 const infinint & repeat_byte, //< how much byte remains to waste for saving again a changing file 71 const infinint & hole_size, //< the threshold for hole detection, set to zero completely disable the sparse file detection mechanism 72 semaphore * sem, 73 infinint & new_wasted_bytes); //< new amount of wasted bytes to return to the caller. 74 75 static bool save_ea(user_interaction & dialog, 76 const string & info_quoi, 77 cat_inode * & ino, 78 const pile_descriptor & pdesc, 79 bool display_treated); 80 81 static void restore_atime(const string & chemin, const cat_inode * & ptr); 82 83 static bool save_fsa(user_interaction & dialog, 84 const string & info_quoi, 85 cat_inode * & ino, 86 const pile_descriptor & pdesc, 87 bool display_treated); 88 89 /// merge two sets of EA 90 91 /// \param[in] ref1 is the first EA set 92 /// \param[in] ref2 is the second EA set 93 /// \param[in,out] res is the EA set result of the merging operation 94 /// \note result is the set of EA of ref1 to which those of ref2 are added if not already present in ref1 95 static void merge_ea(const ea_attributs & ref1, const ea_attributs & ref2, ea_attributs &res); 96 97 /// to clone an "cat_entree" taking hard links into account 98 99 /// \param[in] ref is the named entry to be cloned 100 /// \param[in,out] hard_link_base is the datastructure that gather/maps hard_links information 101 /// \param[in] etiquette_offset is the offset to apply to etiquette (to not mix several hard-link sets using the same etiquette number in different archives) 102 /// \return a pointer to the new allocated clone object (to be deleted by the delete operator by the caller) 103 static cat_entree *make_clone(const cat_nomme *ref, 104 memory_pool *pool, 105 map<infinint, cat_etoile*> & hard_link_base, 106 const infinint & etiquette_offset); 107 108 /// remove an entry hardlink from a given hard_link database 109 110 /// \param[in] mir is a pointer to the cat_mirage object to delete 111 /// \param[in,out] hard_link_base is the datastructure used to gather/map hard_links information 112 /// \note if the cat_mirage object is the last one pointing to a given "cat_etoile" object 113 /// deleting this cat_mirage will delete the "cat_etoile" object automatically (see destructor implementation of these classes). 114 /// However, one need to remove from the database the reference to this "cat_etoile *" that is about to me removed by the caller 115 static void clean_hard_link_base_from(const cat_mirage *mir, map<infinint, cat_etoile *> & hard_link_base); 116 117 118 /// modify the hard_link_base to avoid hole in the numbering of etiquettes (map size == highest etiquette number) 119 static void normalize_link_base(map<infinint, cat_etoile *> & hard_link_base); 120 121 /// transfer EA from one cat_inode to another as defined by the given action 122 123 /// \param[in] dialog for user interaction 124 /// \param[in] action is the action to perform with EA and FSA 125 /// \param[in,out] in_place is the "in place" cat_inode, the resulting EA/FSA operation is placed as EA/FSA of this object, argument may be nullptr 126 /// \param[in] to_add is the "to be added" cat_inode 127 /// \note actions set to EA_preserve EA_preserve_mark_already_saved and EA_clear are left intentionnaly unimplemented! 128 /// \note the nullptr given as argument means that the object is not an cat_inode 129 130 static void do_EFSA_transfert(user_interaction &dialog, 131 memory_pool *pool, 132 over_action_ea action, 133 cat_inode *in_place, 134 const cat_inode *to_add); 135 136 137 /// overwriting policy when only restoring detruit objects 138 static const crit_action *make_overwriting_for_only_deleted(memory_pool *pool); 139 filtre_restore(user_interaction & dialog,memory_pool * pool,const mask & filtre,const mask & subtree,const catalogue & cat,const path & fs_racine,bool fs_warn_overwrite,bool info_details,bool display_treated,bool display_treated_only_dir,bool display_skipped,statistics & st,const mask & ea_mask,bool flat,cat_inode::comparison_fields what_to_check,bool warn_remove_no_match,bool empty,bool empty_dir,const crit_action & x_overwrite,archive_options_extract::t_dirty dirty,bool only_deleted,bool not_deleted,const fsa_scope & scope)140 void filtre_restore(user_interaction & dialog, 141 memory_pool *pool, 142 const mask & filtre, 143 const mask & subtree, 144 const catalogue & cat, 145 const path & fs_racine, 146 bool fs_warn_overwrite, 147 bool info_details, 148 bool display_treated, 149 bool display_treated_only_dir, 150 bool display_skipped, 151 statistics & st, 152 const mask & ea_mask, 153 bool flat, 154 cat_inode::comparison_fields what_to_check, 155 bool warn_remove_no_match, 156 bool empty, 157 bool empty_dir, 158 const crit_action & x_overwrite, 159 archive_options_extract::t_dirty dirty, 160 bool only_deleted, 161 bool not_deleted, 162 const fsa_scope & scope) 163 { 164 defile juillet = fs_racine; // 'juillet' is in reference to 14th of July ;-) when takes place the "defile'" on the Champs-Elysees. 165 const cat_eod tmp_eod; 166 const cat_entree *e; 167 thread_cancellation thr_cancel; 168 const crit_action * when_only_deleted = only_deleted ? make_overwriting_for_only_deleted(pool) : nullptr; 169 const crit_action & overwrite = only_deleted ? *when_only_deleted : x_overwrite; 170 171 if(display_treated_only_dir && display_treated) 172 display_treated = false; 173 // avoid having filesystem to report action performed for each entry 174 // specific code in this function will show instead the current directory 175 // under which file are processed 176 else 177 display_treated_only_dir = false; // avoid incoherence 178 179 try 180 { 181 filesystem_restore fs = filesystem_restore(dialog, 182 fs_racine, 183 fs_warn_overwrite, 184 display_treated, 185 ea_mask, 186 what_to_check, 187 warn_remove_no_match, 188 empty, 189 &overwrite, 190 only_deleted, 191 scope); 192 // if only_deleted, we set the filesystem to only overwrite mode (no creatation if not existing) 193 // we also filter to only restore directories and detruit objects. 194 195 st.clear(); 196 cat.reset_read(); 197 198 if(!empty_dir) 199 cat.launch_recursive_has_changed_update(); 200 201 while(cat.read(e)) 202 { 203 const cat_nomme *e_nom = dynamic_cast<const cat_nomme *>(e); 204 const cat_directory *e_dir = dynamic_cast<const cat_directory *>(e); 205 const cat_mirage *e_mir = dynamic_cast<const cat_mirage *>(e); 206 const cat_inode *e_ino = dynamic_cast<const cat_inode *>(e); 207 const cat_file *e_file = dynamic_cast<const cat_file *>(e); 208 const cat_detruit *e_det = dynamic_cast<const cat_detruit *>(e); 209 210 if(e_mir != nullptr) 211 { 212 e_ino = const_cast<const cat_inode *>(e_mir->get_inode()); 213 if(e_ino == nullptr) 214 throw SRC_BUG; // !?! how is this possible ? 215 e_mir->get_inode()->change_name(e_mir->get_name()); // temporarily changing the inode name to the one of the cat_mirage 216 e_file = dynamic_cast<const cat_file *>(e_ino); 217 } 218 219 if(e_det != nullptr && not_deleted) 220 continue; // skip this cat_detruit object 221 222 juillet.enfile(e); 223 thr_cancel.check_self_cancellation(); 224 if(display_treated_only_dir) 225 { 226 if(e_dir != nullptr) 227 dialog.warning(string(gettext("Inspecting directory ")) + juillet.get_string()); 228 } 229 230 if(e_nom != nullptr) 231 { 232 try 233 { 234 bool path_covered = subtree.is_covered(juillet.get_path()); // is current entry covered by filters (path) 235 bool name_covered = e_dir != nullptr || filtre.is_covered(e_nom->get_name()); // is current entry's filename covered (not applied to directories) 236 bool dirty_covered = e_file == nullptr || !e_file->is_dirty() || dirty != archive_options_extract::dirty_ignore; // checking against dirty files 237 bool empty_dir_covered = e_dir == nullptr || empty_dir || e_dir->get_recursive_has_changed(); // checking whether this is not a directory without any file to restore in it 238 bool flat_covered = e_dir == nullptr || !flat; // we do not restore directories in flat mode 239 bool only_deleted_covered = !only_deleted || e_dir != nullptr || e_det != nullptr; // we do not restore other thing that directories and cat_detruits when in "only_deleted" mode 240 241 if(path_covered && name_covered && dirty_covered && empty_dir_covered && flat_covered && only_deleted_covered) 242 { 243 filesystem_restore::action_done_for_data data_restored = filesystem_restore::done_no_change_no_data; // will be true if file's data have been restored (depending on overwriting policy) 244 bool ea_restored = false; // will be true if file's EA have been restored (depending on overwriting policy) 245 bool hard_link = false; // will be true if data_restored is true and only lead to a hard link creation or file replacement by a hard link to an already existing inode 246 bool fsa_restored = false; // will be true if file's FSA have been restored (depending on overwriting policy) 247 bool first_time = true; // if quite dirty file (saved several time), need to record in sequential reading whether this is the first time we restore it (not used in direct access reading). 248 bool created_retry; // when restoring several dirty copies (in sequential read), we not forget whether the file 249 // has been created or not the first time (which is kept in "created"), for each try, we thus use created_retry 250 // to carry the info whether the file has been created at each try 251 252 struct cached_Erange 253 { 254 bool active; 255 string source; 256 string message; 257 cached_Erange() { active = false; }; 258 } tmp_exc; // used to hold exception information, when restoring in sequential reading in the hope another copy is available for the file 259 260 if(dirty == archive_options_extract::dirty_warn && e_file != nullptr && e_file->is_dirty()) 261 { 262 string tmp = juillet.get_string(); 263 dialog.pause(tools_printf(gettext("File %S has changed during backup and is probably not saved in a valid state (\"dirty file\"), do you want to consider it for restoration anyway?"), &tmp)); 264 } 265 266 do 267 { 268 if(!first_time) // a second time only occures in sequential read mode 269 { 270 const cat_file *e_file = dynamic_cast<const cat_file *>(e_ino); 271 272 if(info_details) 273 dialog.warning(string(gettext("File had changed during backup and had been copied another time, restoring the next copy of file: ")) + juillet.get_string()); 274 275 // we must let the filesystem object forget that 276 // this hard linked inode has already been seen 277 if(e_mir != nullptr) 278 fs.clear_corres_if_pointing_to(e_mir->get_etiquette(), juillet.get_string()); 279 280 if(e_file != nullptr) 281 { 282 cat_file *me_file = const_cast<cat_file *>(e_file); 283 284 if(me_file == nullptr) 285 throw SRC_BUG; 286 287 me_file->drop_crc(); 288 me_file->set_storage_size(0); 289 if(cat.get_escape_layer() == nullptr) 290 throw SRC_BUG; 291 me_file->set_offset(cat.get_escape_layer()->get_position()); 292 } 293 294 fs.ignore_overwrite_restrictions_for_next_write(); 295 } 296 297 try 298 { 299 fs.write(e, data_restored, ea_restored, created_retry, hard_link, fsa_restored); // e and not e_ino, it may be a hard link 300 if(tmp_exc.active) 301 { // restoration succeeded so we drop any pending exception 302 tmp_exc.active = false; 303 } 304 } 305 catch(Erange & e) 306 { 307 // we do not throw the exception right now, but 308 // we let a chance to read a new copy (retry upon change) 309 // of that file. Only if there is no more copy available 310 // we will throw this exception (which we cannot not 311 // at this time if following sequential reading mode. 312 tmp_exc.active = true; 313 tmp_exc.source = e.get_source(); 314 tmp_exc.message = e.get_message(); 315 } 316 317 if(first_time) 318 first_time = false; 319 // Now checking whether there is not a second copy 320 // of the file to use instead, due to file change 321 // while being read for backup. The last copy is the one 322 // to use for restoration. 323 } 324 while(cat.get_escape_layer() != nullptr && cat.get_escape_layer()->skip_to_next_mark(escape::seqt_changed, false) && data_restored == filesystem_restore::done_data_restored); 325 326 if(tmp_exc.active) 327 throw Erange(tmp_exc.source, tmp_exc.message); 328 329 if(cat.get_escape_layer() != nullptr && cat.get_escape_layer()->skip_to_next_mark(escape::seqt_dirty, false)) 330 { 331 string tmp = juillet.get_string(); 332 // file is dirty and we are reading archive sequentially, thus this is only now once we have restored the file that 333 // we can inform the user about the dirtyness of the file. 334 335 if(e_nom == nullptr) 336 throw SRC_BUG; 337 cat_detruit killer = *e_nom; 338 bool tmp_ea_restored, tmp_created_retry, tmp_hard_link, tmp_fsa_restored; 339 filesystem_restore::action_done_for_data tmp_data_restored; 340 341 switch(dirty) 342 { 343 case archive_options_extract::dirty_warn: 344 dialog.pause(tools_printf(gettext("The just restored file %S has been marked as dirty (sequential reading can only detect the dirty status after restoration), do we remove this just restored dirty file?"), &tmp)); 345 // NO BREAK HERE !!! This is intended. 346 case archive_options_extract::dirty_ignore: 347 // we must remove the file 348 if(info_details) 349 { 350 if(dirty == archive_options_extract::dirty_ignore) 351 dialog.warning(tools_printf(gettext("The just restored file %S has been marked as dirty (sequential reading can only detect the dirty status after restoration), removing the just restored dirty file as it is asked to ignore this type of file"), &tmp)); 352 else 353 dialog.warning(tools_printf(gettext("Removing the dirty file %S"), &tmp)); 354 } 355 fs.ignore_overwrite_restrictions_for_next_write(); 356 fs.write(&killer, tmp_data_restored, tmp_ea_restored, tmp_created_retry, tmp_hard_link, tmp_fsa_restored); 357 break; 358 case archive_options_extract::dirty_ok: 359 break; 360 default: 361 throw SRC_BUG; 362 } 363 } 364 365 // Now updating the statistics counters 366 367 if(hard_link) 368 st.incr_hard_links(); 369 370 switch(data_restored) 371 { 372 case filesystem_restore::done_data_restored: 373 if(e_dir == nullptr || !cat.read_second_time_dir()) 374 st.incr_treated(); 375 break; 376 case filesystem_restore::done_no_change_no_data: 377 st.incr_skipped(); 378 break; 379 case filesystem_restore::done_no_change_policy: 380 st.incr_tooold(); 381 break; 382 case filesystem_restore::done_data_removed: 383 st.incr_deleted(); 384 break; 385 default: 386 throw SRC_BUG; 387 } 388 389 if(ea_restored) 390 st.incr_ea_treated(); 391 392 if(fsa_restored) 393 st.incr_fsa_treated(); 394 } 395 else // oject not covered by filters 396 { 397 if(display_skipped) 398 { 399 if(!path_covered || !name_covered || !dirty_covered) 400 dialog.warning(string(gettext(SKIPPED)) + juillet.get_string()); 401 else 402 dialog.warning(string(gettext(SQUEEZED)) + juillet.get_string()); 403 } 404 405 if(e_dir == nullptr || !cat.read_second_time_dir()) 406 st.incr_ignored(); 407 if(e_dir != nullptr) 408 { 409 if(!path_covered || !empty_dir_covered) 410 { 411 // this directory has been excluded by path_covered 412 // or empty_dir_covered. We must not recurse in it 413 // (this is not a flat restoration for example) 414 cat.skip_read_to_parent_dir(); 415 juillet.enfile(&tmp_eod); 416 } 417 } 418 } 419 } 420 catch(Ebug & e) 421 { 422 throw; 423 } 424 catch(Euser_abort & e) 425 { 426 dialog.warning(juillet.get_string() + gettext(" not restored (user choice)")); 427 428 if(e_dir != nullptr && !flat) 429 { 430 dialog.warning(gettext("No file in this directory will be restored.")); 431 cat.skip_read_to_parent_dir(); 432 juillet.enfile(&tmp_eod); 433 } 434 if(e_dir == nullptr || !cat.read_second_time_dir()) 435 st.incr_ignored(); 436 } 437 catch(Ethread_cancel & e) 438 { 439 throw; 440 } 441 catch(Escript & e) 442 { 443 throw; 444 } 445 catch(Egeneric & e) 446 { 447 if(!only_deleted || e_dir == nullptr) 448 dialog.warning(string(gettext("Error while restoring ")) + juillet.get_string() + " : " + e.get_message()); 449 450 if(e_dir != nullptr && !flat) 451 { 452 if(!only_deleted) 453 dialog.warning(string(gettext("Warning! No file in that directory will be restored: ")) + juillet.get_string()); 454 cat.skip_read_to_parent_dir(); 455 juillet.enfile(&tmp_eod); 456 } 457 if(e_dir == nullptr || !cat.read_second_time_dir()) 458 if(!only_deleted) 459 st.incr_errored(); 460 } 461 } 462 else // e_nom == nullptr : this should be a CAT_EOD 463 { 464 const cat_eod *e_eod = dynamic_cast<const cat_eod *>(e); 465 466 if(e_eod == nullptr) 467 throw SRC_BUG; // not an class cat_eod object, nor a class cat_nomme object ??? 468 469 if(!flat) 470 { 471 bool notusedhere; 472 filesystem_restore::action_done_for_data tmp; 473 fs.write(e, tmp, notusedhere, notusedhere, notusedhere, notusedhere); // cat_eod; don't care returned value 474 } 475 } 476 } 477 } 478 catch(...) 479 { 480 if(when_only_deleted != nullptr) 481 delete when_only_deleted; 482 throw; 483 } 484 if(when_only_deleted != nullptr) 485 delete when_only_deleted; 486 } 487 filtre_sauvegarde(user_interaction & dialog,memory_pool * pool,const mask & filtre,const mask & subtree,const pile_descriptor & pdesc,catalogue & cat,const catalogue & ref,const path & fs_racine,bool info_details,bool display_treated,bool display_treated_only_dir,bool display_skipped,bool display_finished,statistics & st,bool make_empty_dir,const mask & ea_mask,const mask & compr_mask,const infinint & min_compr_size,bool nodump,const infinint & hourshift,bool alter_atime,bool furtive_read_mode,bool same_fs,cat_inode::comparison_fields what_to_check,bool snapshot,bool cache_directory_tagging,bool security_check,const infinint & repeat_count,const infinint & repeat_byte,const infinint & fixed_date,const infinint & sparse_file_min_size,const string & backup_hook_file_execute,const mask & backup_hook_file_mask,bool ignore_unknown,const fsa_scope & scope,const string & exclude_by_ea,bool auto_zeroing_neg_dates)488 void filtre_sauvegarde(user_interaction & dialog, 489 memory_pool *pool, 490 const mask &filtre, 491 const mask &subtree, 492 const pile_descriptor & pdesc, 493 catalogue & cat, 494 const catalogue & ref, 495 const path & fs_racine, 496 bool info_details, 497 bool display_treated, 498 bool display_treated_only_dir, 499 bool display_skipped, 500 bool display_finished, 501 statistics & st, 502 bool make_empty_dir, 503 const mask & ea_mask, 504 const mask &compr_mask, 505 const infinint & min_compr_size, 506 bool nodump, 507 const infinint & hourshift, 508 bool alter_atime, 509 bool furtive_read_mode, 510 bool same_fs, 511 cat_inode::comparison_fields what_to_check, 512 bool snapshot, 513 bool cache_directory_tagging, 514 bool security_check, 515 const infinint & repeat_count, 516 const infinint & repeat_byte, 517 const infinint & fixed_date, 518 const infinint & sparse_file_min_size, 519 const string & backup_hook_file_execute, 520 const mask & backup_hook_file_mask, 521 bool ignore_unknown, 522 const fsa_scope & scope, 523 const string & exclude_by_ea, 524 bool auto_zeroing_neg_dates) 525 { 526 cat_entree *e = nullptr; 527 const cat_entree *f = nullptr; 528 defile juillet = fs_racine; 529 const cat_eod tmp_eod; 530 compression stock_algo; 531 semaphore sem = semaphore(dialog, backup_hook_file_execute, backup_hook_file_mask); 532 533 if(display_treated_only_dir && display_treated) 534 display_treated = false; 535 // avoid having save_inode/save_ea to report action performed for each entry 536 // specific code in this function will show instead the current directory 537 // under which file are processed 538 else 539 display_treated_only_dir = false; // avoid incoherence 540 541 542 stock_algo = pdesc.compr->get_algo(); 543 infinint root_fs_device; 544 filesystem_backup fs = filesystem_backup(dialog, 545 fs_racine, 546 info_details, 547 ea_mask, 548 nodump, 549 alter_atime, 550 furtive_read_mode, 551 cache_directory_tagging, 552 root_fs_device, 553 ignore_unknown, 554 scope); 555 thread_cancellation thr_cancel; 556 infinint skipped_dump, fs_errors; 557 infinint wasted_bytes = 0; 558 559 if(auto_zeroing_neg_dates) 560 fs.zeroing_negative_dates_without_asking(); 561 562 st.clear(); 563 cat.reset_add(); 564 ref.reset_compare(); 565 566 try 567 { 568 try 569 { 570 while(fs.read(e, fs_errors, skipped_dump)) 571 { 572 cat_nomme *nom = dynamic_cast<cat_nomme *>(e); 573 cat_directory *dir = dynamic_cast<cat_directory *>(e); 574 cat_inode *e_ino = dynamic_cast<cat_inode *>(e); 575 cat_file *e_file = dynamic_cast<cat_file *>(e); 576 cat_mirage *e_mir = dynamic_cast<cat_mirage *>(e); 577 bool known_hard_link = false; 578 579 st.add_to_ignored(skipped_dump); 580 st.add_to_errored(fs_errors); 581 582 juillet.enfile(e); 583 thr_cancel.check_self_cancellation(); 584 if(display_treated_only_dir) 585 { 586 if(dir != nullptr) 587 dialog.warning(string(gettext("Inspecting directory ")) + juillet.get_string()); 588 } 589 590 if(e_mir != nullptr) 591 { 592 known_hard_link = e_mir->is_inode_wrote(); 593 if(!known_hard_link) 594 { 595 e_ino = dynamic_cast<cat_inode *>(e_mir->get_inode()); 596 e_file = dynamic_cast<cat_file *>(e_mir->get_inode()); 597 if(e_ino == nullptr) 598 throw SRC_BUG; 599 e_ino->change_name(e_mir->get_name()); 600 } 601 } 602 603 if(nom != nullptr) 604 { 605 try 606 { 607 string tmp_val; 608 609 if(subtree.is_covered(juillet.get_path()) 610 && (dir != nullptr || filtre.is_covered(nom->get_name())) 611 && (! same_fs || e_ino == nullptr || e_ino->get_device() == root_fs_device) 612 && (e_ino == nullptr || exclude_by_ea == "" || e_ino->ea_get_saved_status() != cat_inode::ea_full || e_ino->get_ea() == nullptr || !e_ino->get_ea()->find(exclude_by_ea, tmp_val))) 613 { 614 if(known_hard_link) 615 { 616 // no need to update the semaphore here as no data is read and no directory is expected here 617 cat.pre_add(e); // if cat is a escape_catalogue, this adds an escape sequence and entry info in the archive 618 cat.add(e); 619 e = nullptr; 620 st.incr_hard_links(); 621 st.incr_treated(); 622 if(e_mir != nullptr) 623 { 624 if(e_mir->get_inode()->get_saved_status() == s_saved || e_mir->get_inode()->ea_get_saved_status() == cat_inode::ea_full) 625 if(display_treated) 626 dialog.warning(string(gettext("Recording hard link into the archive: "))+juillet.get_string()); 627 } 628 else 629 throw SRC_BUG; // known_hard_link is true and e_mir == nullptr !??? 630 } 631 else // not a hard link or known hard linked inode 632 { 633 const cat_inode *f_ino = nullptr; 634 const cat_file *f_file = nullptr; 635 const cat_mirage *f_mir = nullptr; 636 637 if(e_ino == nullptr) 638 throw SRC_BUG; // if not a known hard link, e_ino should still either point to a real cat_inode 639 // or to the hard linked new cat_inode. 640 641 if(fixed_date.is_zero()) 642 { 643 bool conflict = ref.compare(e, f); 644 645 if(!conflict) 646 { 647 f = nullptr; 648 f_ino = nullptr; 649 f_mir = nullptr; 650 } 651 else // inode was already present in filesystem at the time the archive of reference was made 652 { 653 f_ino = dynamic_cast<const cat_inode *>(f); 654 f_file = dynamic_cast<const cat_file *>(f); 655 f_mir = dynamic_cast<const cat_mirage *>(f); 656 657 if(f_mir != nullptr) 658 { 659 f_ino = f_mir->get_inode(); 660 f_file = dynamic_cast<const cat_file *>(f_ino); 661 } 662 663 // Now checking for filesystem dissimulated modifications 664 665 if(security_check) 666 { 667 if(f_ino != nullptr && e_ino != nullptr) 668 { 669 // both are inodes 670 671 if(compatible_signature(f_ino->signature(), e_ino->signature()) 672 // both are of the same type of inode 673 && f_file != nullptr) 674 // both are plain files (no warning issued for other inode types) 675 { 676 // both are plain file or hard-linked plain files 677 678 if(f_ino->get_uid() == e_ino->get_uid() 679 && f_ino->get_gid() == e_ino->get_gid() 680 && f_ino->get_perm() == e_ino->get_perm() 681 && f_ino->get_last_modif() == e_ino->get_last_modif()) // no datetime::loose_equal() here! 682 { 683 // same inode information 684 685 if(f_ino->has_last_change() && e_ino->has_last_change()) 686 { 687 // both inode ctime has been recorded 688 689 if(!f_ino->get_last_change().loose_equal(e_ino->get_last_change())) 690 { 691 string tmp = juillet.get_string(); 692 693 dialog.printf(gettext("SECURITY WARNING! SUSPICIOUS FILE %S: ctime changed since archive of reference was done, while no other inode information changed"), &tmp); 694 } 695 } 696 } 697 } 698 } 699 } 700 } 701 } 702 else 703 f = nullptr; 704 705 try 706 { 707 f_ino = snapshot ? nullptr : f_ino; 708 f_file = snapshot ? nullptr : f_file; 709 710 // EVALUATING THE ACTION TO PERFORM 711 712 bool change_to_remove_ea = 713 e_ino != nullptr && e_ino->ea_get_saved_status() == cat_inode::ea_none 714 // current inode to backup does not have any EA 715 && f_ino != nullptr && f_ino->ea_get_saved_status() != cat_inode::ea_none 716 && f_ino->ea_get_saved_status() != cat_inode::ea_removed; 717 // and reference was an inode with EA 718 719 bool avoid_saving_inode = 720 snapshot 721 // don't backup if doing a snapshot 722 || (!fixed_date.is_zero() && e_ino != nullptr && e_ino->get_last_modif() < fixed_date) 723 // don't backup if older than given date (if reference date given) 724 || (fixed_date.is_zero() && e_ino != nullptr && f_ino != nullptr && !e_ino->has_changed_since(*f_ino, hourshift, what_to_check) 725 && (f_file == nullptr || !f_file->is_dirty())) 726 // don't backup if doing differential backup and entry is the same as the one in the archive of reference 727 // and if the reference is a plain file, it was not saved as dirty 728 ; 729 730 bool avoid_saving_ea = 731 snapshot 732 // don't backup if doing a snapshot 733 || (!fixed_date.is_zero() && e_ino != nullptr && e_ino->ea_get_saved_status() != cat_inode::ea_none && e_ino->get_last_change() < fixed_date) 734 // don't backup if older than given date (if reference date given) 735 || (fixed_date.is_zero() && e_ino != nullptr && e_ino->ea_get_saved_status() == cat_inode::ea_full && f_ino != nullptr && f_ino->ea_get_saved_status() != cat_inode::ea_none && e_ino->get_last_change() <= f_ino->get_last_change()) 736 // don't backup if doing differential backup and entry is the same as the one in the archive of reference 737 ; 738 739 bool avoid_saving_fsa = 740 snapshot 741 // don't backup if doing a snapshot 742 || (!fixed_date.is_zero() && e_ino != nullptr && e_ino->fsa_get_saved_status() != cat_inode::fsa_none && e_ino->get_last_change() < fixed_date) 743 // don't backup if older than given date (if reference date given) 744 || (fixed_date.is_zero() && e_ino != nullptr && e_ino->fsa_get_saved_status() == cat_inode::fsa_full && f_ino != nullptr && f_ino->fsa_get_saved_status() != cat_inode::fsa_none && e_ino->get_last_change() <= f_ino->get_last_change()) 745 // don't backup if doing differential backup and entry is the same as the one in the archive of reference 746 ; 747 748 bool sparse_file_detection = 749 e_file != nullptr 750 && e_file->get_size() > sparse_file_min_size 751 && !sparse_file_min_size.is_zero(); 752 753 // MODIFIYING INODE IF NECESSARY 754 755 if(e_ino->get_saved_status() != s_saved) 756 throw SRC_BUG; // filsystem should always provide "saved" "cat_entree" 757 758 if(avoid_saving_inode) 759 { 760 e_ino->set_saved_status(s_not_saved); 761 st.incr_skipped(); 762 } 763 764 if(avoid_saving_ea) 765 { 766 if(e_ino->ea_get_saved_status() == cat_inode::ea_full) 767 e_ino->ea_set_saved_status(cat_inode::ea_partial); 768 } 769 770 if(avoid_saving_fsa) 771 { 772 if(e_ino->fsa_get_saved_status() == cat_inode::fsa_full) 773 e_ino->fsa_set_saved_status(cat_inode::fsa_partial); 774 } 775 776 if(change_to_remove_ea) 777 e_ino->ea_set_saved_status(cat_inode::ea_removed); 778 779 if(e_file != nullptr) 780 e_file->set_sparse_file_detection_write(sparse_file_detection); 781 782 // DECIDING WHETHER FILE DATA WILL BE COMPRESSED OR NOT 783 784 if(e_file != nullptr) 785 { 786 if(compr_mask.is_covered(nom->get_name()) && e_file->get_size() >= min_compr_size) 787 // e_nom not e_file because "e" 788 // may be a hard link, in which case its name is not carried by e_ino nor e_file 789 e_file->change_compression_algo_write(stock_algo); 790 else 791 e_file->change_compression_algo_write(none); 792 } 793 794 795 // PERFORMING ACTION FOR ENTRY (cat_entree dump, eventually data dump) 796 797 if(!save_inode(dialog, 798 pool, 799 juillet.get_string(), 800 e, 801 pdesc, 802 info_details, 803 display_treated, 804 alter_atime, 805 true, // check_change 806 true, // compute_crc 807 cat_file::normal, // keep_mode 808 cat, 809 repeat_count, 810 repeat_byte, 811 sparse_file_min_size, 812 &sem, 813 wasted_bytes)) 814 st.incr_tooold(); // counting a new dirty file in archive 815 816 817 st.set_byte_amount(wasted_bytes); 818 819 if(!avoid_saving_inode) 820 st.incr_treated(); 821 822 // PERFORMING ACTION FOR EA 823 824 if(e_ino->ea_get_saved_status() != cat_inode::ea_removed) 825 { 826 if(e_ino->ea_get_saved_status() == cat_inode::ea_full) 827 cat.pre_add_ea(e); 828 if(save_ea(dialog, juillet.get_string(), e_ino, pdesc, display_treated)) 829 st.incr_ea_treated(); 830 cat.pre_add_ea_crc(e); 831 } 832 833 // PERFORMING ACTION FOR FSA 834 835 if(e_ino->fsa_get_saved_status() == cat_inode::fsa_full) 836 { 837 cat.pre_add_fsa(e); 838 if(save_fsa(dialog, juillet.get_string(), e_ino, pdesc, display_treated)) 839 st.incr_fsa_treated(); 840 cat.pre_add_fsa_crc(e); 841 } 842 843 // CLEANING UP MEMORY FOR PLAIN FILES 844 845 if(e_file != nullptr) 846 e_file->clean_data(); 847 848 // UPDATING HARD LINKS 849 850 if(e_mir != nullptr) 851 e_mir->set_inode_wrote(true); // record that this inode has been saved (it is a "known_hard_link" now) 852 853 // ADDING ENTRY TO CATALOGUE 854 855 cat.add(e); 856 e = nullptr; 857 } 858 catch(...) 859 { 860 if(dir != nullptr && fixed_date.is_zero()) 861 ref.compare(&tmp_eod, f); 862 throw; 863 } 864 } 865 } 866 else // inode not covered 867 { 868 cat_nomme *ig = nullptr; 869 cat_inode *ignode = nullptr; 870 sem.raise(juillet.get_string(), e, false); 871 872 if(display_skipped) 873 dialog.warning(string(gettext(SKIPPED)) + juillet.get_string()); 874 875 if(dir != nullptr && make_empty_dir) 876 ig = ignode = new (pool) cat_ignored_dir(*dir); 877 else 878 ig = new (pool) cat_ignored(nom->get_name()); 879 // necessary to not record deleted files at comparison 880 // time in case files are just not covered by filters 881 st.incr_ignored(); 882 883 if(ig == nullptr) 884 throw Ememory("filtre_sauvegarde"); 885 else 886 cat.add(ig); 887 888 if(dir != nullptr) 889 { 890 if(make_empty_dir) 891 { 892 bool known; 893 894 if(fixed_date.is_zero()) 895 known = ref.compare(dir, f); 896 else 897 known = false; 898 899 try 900 { 901 const cat_inode *f_ino = known ? dynamic_cast<const cat_inode *>(f) : nullptr; 902 bool tosave = false; 903 904 if(known) 905 if(f_ino != nullptr) 906 tosave = dir->has_changed_since(*f_ino, hourshift, what_to_check); 907 else 908 throw SRC_BUG; 909 // catalogue::compare() with a directory should return false or give a directory as 910 // second argument or here f is not an inode (f_ino == nullptr) ! 911 // and known == true 912 else 913 tosave = true; 914 915 ignode->set_saved_status(tosave && !snapshot ? s_saved : s_not_saved); 916 } 917 catch(...) 918 { 919 if(fixed_date.is_zero()) 920 ref.compare(&tmp_eod, f); 921 throw; 922 } 923 if(fixed_date.is_zero()) 924 ref.compare(&tmp_eod, f); 925 } 926 fs.skip_read_to_parent_dir(); 927 juillet.enfile(&tmp_eod); 928 } 929 930 delete e; // we don't keep this object in the catalogue so we must destroy it 931 e = nullptr; 932 } 933 } 934 catch(Ebug & e) 935 { 936 throw; 937 } 938 catch(Euser_abort & e) 939 { 940 throw; 941 } 942 catch(Escript & e) 943 { 944 throw; 945 } 946 catch(Ethread_cancel & e) 947 { 948 throw; 949 } 950 catch(Elimitint & e) 951 { 952 throw; 953 } 954 catch(Egeneric & ex) 955 { 956 const string & how = ex.find_object("generic_file::copy_to"); 957 958 if(how != "write") // error did not occured while adding data to the archive 959 { 960 cat_nomme *tmp = new (pool) cat_ignored(nom->get_name()); 961 dialog.warning(string(gettext("Error while saving ")) + juillet.get_string() + ": " + ex.get_message()); 962 st.incr_errored(); 963 964 // now we can destroy the object 965 delete e; 966 e = nullptr; 967 968 if(tmp == nullptr) 969 throw Ememory("fitre_sauvegarde"); 970 cat.add(tmp); 971 972 if(dir != nullptr) 973 { 974 fs.skip_read_to_parent_dir(); 975 juillet.enfile(&tmp_eod); 976 dialog.warning(gettext("NO FILE IN THAT DIRECTORY CAN BE SAVED.")); 977 } 978 } 979 else 980 { 981 ex.prepend_message(gettext("Cannot write down the archive: ")); 982 throw; // error occured while adding data to the archive, archive cannot be generated properly 983 } 984 } 985 } 986 else // cat_eod 987 { 988 sem.raise(juillet.get_string(), e, true); 989 sem.lower(); 990 if(fixed_date.is_zero()) 991 ref.compare(e, f); // makes the comparison in the reference catalogue go to parent directory 992 cat.pre_add(e); // adding a mark and dropping CAT_EOD entry in the archive if cat is an escape_catalogue object (else, does nothing) 993 if(display_finished) 994 { 995 const cat_directory & cur = cat.get_current_add_dir(); 996 string what = juillet.get_string(); 997 string size = tools_display_integer_in_metric_system(cur.get_size(), "o", true); 998 string ratio = gettext(", compression ratio "); 999 1000 if(!cur.get_storage_size().is_zero()) 1001 ratio += tools_get_compression_ratio(cur.get_storage_size(), cur.get_size(), true); 1002 else 1003 ratio = ""; 1004 dialog.printf(gettext("Finished Inspecting directory %S , saved %S%S"), 1005 &what, 1006 &size, 1007 &ratio); 1008 } 1009 cat.add(e); 1010 1011 } 1012 } 1013 } 1014 catch(Ethread_cancel & e) 1015 { 1016 if(!e.immediate_cancel()) 1017 { 1018 if(pdesc.compr->is_compression_suspended()) 1019 { 1020 pdesc.stack->sync_write_above(pdesc.compr); 1021 pdesc.compr->resume_compression(); 1022 } 1023 } 1024 throw Ethread_cancel_with_attr(e.immediate_cancel(), e.get_flag(), fs.get_last_etoile_ref()); 1025 } 1026 } 1027 catch(...) 1028 { 1029 if(e != nullptr) 1030 delete e; 1031 throw; 1032 } 1033 1034 if(pdesc.compr->is_compression_suspended()) 1035 { 1036 pdesc.stack->sync_write_above(pdesc.compr); 1037 pdesc.compr->resume_compression(); 1038 } 1039 } 1040 filtre_difference(user_interaction & dialog,memory_pool * pool,const mask & filtre,const mask & subtree,const catalogue & cat,const path & fs_racine,bool info_details,bool display_treated,bool display_treated_only_dir,bool display_skipped,statistics & st,const mask & ea_mask,bool alter_atime,bool furtive_read_mode,cat_inode::comparison_fields what_to_check,const infinint & hourshift,bool compare_symlink_date,const fsa_scope & scope,bool isolated_mode)1041 void filtre_difference(user_interaction & dialog, 1042 memory_pool *pool, 1043 const mask &filtre, 1044 const mask &subtree, 1045 const catalogue & cat, 1046 const path & fs_racine, 1047 bool info_details, 1048 bool display_treated, 1049 bool display_treated_only_dir, 1050 bool display_skipped, 1051 statistics & st, 1052 const mask & ea_mask, 1053 bool alter_atime, 1054 bool furtive_read_mode, 1055 cat_inode::comparison_fields what_to_check, 1056 const infinint & hourshift, 1057 bool compare_symlink_date, 1058 const fsa_scope & scope, 1059 bool isolated_mode) 1060 { 1061 const cat_entree *e; 1062 defile juillet = fs_racine; 1063 const cat_eod tmp_eod; 1064 filesystem_diff fs = filesystem_diff(dialog, 1065 fs_racine, 1066 info_details, 1067 ea_mask, 1068 alter_atime, 1069 furtive_read_mode, 1070 scope); 1071 thread_cancellation thr_cancel; 1072 1073 if(display_treated_only_dir && display_treated) 1074 display_treated = false; 1075 // avoiding the report of action performed for each entry 1076 // specific code in this function will show instead the current directory 1077 // under which file are processed 1078 else 1079 display_treated_only_dir = false; // avoid incoherence 1080 1081 st.clear(); 1082 cat.reset_read(); 1083 1084 while(cat.read(e)) 1085 { 1086 const cat_directory *e_dir = dynamic_cast<const cat_directory *>(e); 1087 const cat_nomme *e_nom = dynamic_cast<const cat_nomme *>(e); 1088 const cat_inode *e_ino = dynamic_cast<const cat_inode *>(e); 1089 const cat_mirage *e_mir = dynamic_cast<const cat_mirage *>(e); 1090 1091 if(e_mir != nullptr) 1092 { 1093 const cat_file *e_file = dynamic_cast<const cat_file *>(e_mir->get_inode()); 1094 1095 if(e_file == nullptr || e_mir->get_etoile_ref_count() == 1 || cat.get_escape_layer() == nullptr) 1096 { 1097 e_ino = e_mir->get_inode(); 1098 e_mir->get_inode()->change_name(e_mir->get_name()); 1099 } 1100 else 1101 dialog.warning(gettext("SKIPPED (hard link in sequential read mode): ") + e_mir->get_name()); 1102 } 1103 1104 juillet.enfile(e); 1105 thr_cancel.check_self_cancellation(); 1106 if(display_treated_only_dir) 1107 { 1108 if(e_dir != nullptr) 1109 dialog.warning(string(gettext("Inspecting directory ")) + juillet.get_string()); 1110 } 1111 1112 try 1113 { 1114 if(e_nom != nullptr) 1115 { 1116 if(subtree.is_covered(juillet.get_path()) && (e_dir != nullptr || filtre.is_covered(e_nom->get_name()))) 1117 { 1118 cat_nomme *exists_nom = nullptr; 1119 1120 if(e_ino != nullptr) 1121 { 1122 if(e_nom == nullptr) 1123 throw SRC_BUG; 1124 if(fs.read_filename(e_nom->get_name(), exists_nom)) 1125 { 1126 try 1127 { 1128 cat_inode *exists = dynamic_cast<cat_inode *>(exists_nom); 1129 cat_directory *exists_dir = dynamic_cast<cat_directory *>(exists_nom); 1130 1131 if(exists != nullptr) 1132 { 1133 try 1134 { 1135 e_ino->compare(*exists, ea_mask, what_to_check, hourshift, compare_symlink_date, scope, isolated_mode); 1136 if(display_treated) 1137 dialog.warning(string(gettext("OK "))+juillet.get_string()); 1138 if(e_dir == nullptr || !cat.read_second_time_dir()) 1139 st.incr_treated(); 1140 if(!alter_atime) 1141 { 1142 const cat_inode * tmp_exists = const_cast<const cat_inode *>(exists); 1143 restore_atime(juillet.get_string(), tmp_exists); 1144 } 1145 } 1146 catch(Erange & e) 1147 { 1148 dialog.warning(string(gettext("DIFF "))+juillet.get_string()+": "+ e.get_message()); 1149 if(e_dir == nullptr && exists_dir != nullptr) 1150 fs.skip_read_filename_in_parent_dir(); 1151 if(e_dir != nullptr && exists_dir == nullptr) 1152 { 1153 cat.skip_read_to_parent_dir(); 1154 juillet.enfile(&tmp_eod); 1155 } 1156 1157 if(e_dir == nullptr || !cat.read_second_time_dir()) 1158 st.incr_errored(); 1159 if(!alter_atime) 1160 { 1161 const cat_inode * tmp_exists = const_cast<const cat_inode *>(exists); 1162 restore_atime(juillet.get_string(), tmp_exists); 1163 } 1164 } 1165 } 1166 else // existing file is not an inode 1167 throw SRC_BUG; // filesystem, should always return inode with read_filename() 1168 } 1169 catch(...) 1170 { 1171 delete exists_nom; 1172 exists_nom = nullptr; 1173 throw; 1174 } 1175 delete exists_nom; 1176 exists_nom = nullptr; 1177 } 1178 else // can't compare, nothing of that name in filesystem 1179 { 1180 dialog.warning(string(gettext("DIFF "))+ juillet.get_string() + gettext(": file not present in filesystem")); 1181 if(e_dir != nullptr) 1182 { 1183 cat.skip_read_to_parent_dir(); 1184 juillet.enfile(&tmp_eod); 1185 } 1186 1187 if(e_dir == nullptr || !cat.read_second_time_dir()) 1188 st.incr_errored(); 1189 } 1190 } 1191 else // not an inode (for example a cat_detruit, hard_link etc...), nothing to do 1192 st.incr_treated(); 1193 } 1194 else // not covered by filters 1195 { 1196 if(display_skipped) 1197 dialog.warning(string(gettext(SKIPPED)) + juillet.get_string()); 1198 1199 if(e_dir == nullptr || !cat.read_second_time_dir()) 1200 st.incr_ignored(); 1201 if(e_dir != nullptr) 1202 { 1203 cat.skip_read_to_parent_dir(); 1204 juillet.enfile(&tmp_eod); 1205 } 1206 } 1207 } 1208 else // cat_eod ? 1209 if(dynamic_cast<const cat_eod *>(e) != nullptr) // yes cat_eod 1210 fs.skip_read_filename_in_parent_dir(); 1211 else // no ?!? 1212 throw SRC_BUG; // not cat_nomme neither cat_eod ! what's that ? 1213 } 1214 catch(Euser_abort &e) 1215 { 1216 throw; 1217 } 1218 catch(Ebug &e) 1219 { 1220 throw; 1221 } 1222 catch(Escript & e) 1223 { 1224 throw; 1225 } 1226 catch(Ethread_cancel & e) 1227 { 1228 throw; 1229 } 1230 catch(Egeneric & e) 1231 { 1232 dialog.warning(string(gettext("ERR ")) + juillet.get_string() + " : " + e.get_message()); 1233 if(e_dir == nullptr || !cat.read_second_time_dir()) 1234 st.incr_errored(); 1235 } 1236 } 1237 fs.skip_read_filename_in_parent_dir(); 1238 // this call here only to restore dates of the root (-R option) directory 1239 } 1240 filtre_test(user_interaction & dialog,memory_pool * pool,const mask & filtre,const mask & subtree,const catalogue & cat,bool info_details,bool display_treated,bool display_treated_only_dir,bool display_skipped,bool empty,statistics & st)1241 void filtre_test(user_interaction & dialog, 1242 memory_pool *pool, 1243 const mask &filtre, 1244 const mask &subtree, 1245 const catalogue & cat, 1246 bool info_details, 1247 bool display_treated, 1248 bool display_treated_only_dir, 1249 bool display_skipped, 1250 bool empty, 1251 statistics & st) 1252 { 1253 const cat_entree *e; 1254 defile juillet = FAKE_ROOT; 1255 null_file black_hole = null_file(gf_write_only); 1256 infinint offset; 1257 const cat_eod tmp_eod; 1258 thread_cancellation thr_cancel; 1259 string perimeter; 1260 1261 if(display_treated_only_dir && display_treated) 1262 display_treated = false; 1263 // avoid having save_inode/save_ea to report action performed for each entry 1264 // specific code in this function will show instead the current directory 1265 // under which file are processed 1266 else 1267 display_treated_only_dir = false; // avoid incoherence 1268 1269 1270 st.clear(); 1271 cat.set_all_mirage_s_inode_wrote_field_to(false); 1272 cat.reset_read(); 1273 while(cat.read(e)) 1274 { 1275 const cat_file *e_file = dynamic_cast<const cat_file *>(e); 1276 const cat_inode *e_ino = dynamic_cast<const cat_inode *>(e); 1277 const cat_directory *e_dir = dynamic_cast<const cat_directory *>(e); 1278 const cat_nomme *e_nom = dynamic_cast<const cat_nomme *>(e); 1279 const cat_mirage *e_mir = dynamic_cast<const cat_mirage *>(e); 1280 1281 juillet.enfile(e); 1282 thr_cancel.check_self_cancellation(); 1283 if(display_treated_only_dir) 1284 { 1285 if(e_dir != nullptr) 1286 dialog.warning(string(gettext("Inspecting directory ")) + juillet.get_string()); 1287 } 1288 1289 perimeter = ""; 1290 try 1291 { 1292 if(e_mir != nullptr) 1293 { 1294 if(!e_mir->is_inode_wrote()) 1295 { 1296 e_file = dynamic_cast<const cat_file *>(e_mir->get_inode()); 1297 e_ino = e_mir->get_inode(); 1298 } 1299 } 1300 1301 if(e_nom != nullptr) 1302 { 1303 if(subtree.is_covered(juillet.get_path()) && (e_dir != nullptr || filtre.is_covered(e_nom->get_name()))) 1304 { 1305 // checking data file if any 1306 if(e_file != nullptr && e_file->get_saved_status() == s_saved) 1307 { 1308 perimeter = gettext("Data"); 1309 if(!empty) 1310 { 1311 bool dirty_file; 1312 1313 do 1314 { 1315 generic_file *dat = e_file->get_data(cat_file::normal); 1316 if(dat == nullptr) 1317 throw Erange("filtre_test", gettext("Can't read saved data.")); 1318 1319 dirty_file = false; 1320 1321 try 1322 { 1323 infinint crc_size; 1324 crc *check = nullptr; 1325 const crc *original = nullptr; 1326 1327 if(!e_file->get_crc_size(crc_size)) 1328 crc_size = tools_file_size_to_crc_size(e_file->get_size()); 1329 1330 dat->skip(0); 1331 // in sequential read mode, storage_size is zero 1332 // which leads to ask an endless read_ahead (up to eof) 1333 // thus the read_ahaead will be bounded by the escape 1334 // layer up to the next tape mark, as expected 1335 dat->read_ahead(e_file->get_storage_size()); 1336 try 1337 { 1338 dat->copy_to(black_hole, crc_size, check); 1339 } 1340 catch(...) 1341 { 1342 // in sequential read mode we must 1343 // try to read the CRC for the object 1344 // be completed and not generating an 1345 // error due to absence of CRC later on 1346 e_file->get_crc(original); 1347 throw; 1348 } 1349 if(check == nullptr) 1350 throw SRC_BUG; 1351 1352 try 1353 { 1354 // due to possible sequential reading mode, the CRC 1355 // must not be fetched before the data has been copied 1356 if(e_file->get_crc(original)) 1357 { 1358 if(original == nullptr) 1359 throw SRC_BUG; 1360 if(typeid(*check) != typeid(*original)) 1361 throw SRC_BUG; 1362 if(*check != *original) 1363 throw Erange("fitre_test", gettext("CRC error: data corruption.")); 1364 } 1365 } 1366 catch(...) 1367 { 1368 delete check; 1369 throw; 1370 } 1371 delete check; 1372 } 1373 catch(...) 1374 { 1375 delete dat; 1376 throw; 1377 } 1378 delete dat; 1379 1380 if(cat.get_escape_layer() != nullptr 1381 && cat.get_escape_layer()->skip_to_next_mark(escape::seqt_changed, false)) 1382 { 1383 dirty_file = true; 1384 cat_file *modif_e_file = const_cast<cat_file *>(e_file); 1385 if(modif_e_file == nullptr) 1386 throw SRC_BUG; 1387 modif_e_file->drop_crc(); 1388 modif_e_file->set_storage_size(0); 1389 modif_e_file->set_offset(cat.get_escape_layer()->get_position()); 1390 } 1391 } 1392 while(dirty_file); 1393 } 1394 } 1395 1396 // checking inode EA if any 1397 if(e_ino != nullptr && e_ino->ea_get_saved_status() == cat_inode::ea_full) 1398 { 1399 if(perimeter == "") 1400 perimeter = "EA"; 1401 else 1402 perimeter += " + EA"; 1403 if(!empty) 1404 { 1405 ea_attributs tmp = *(e_ino->get_ea()); 1406 perimeter += "(" + deci(tmp.size()).human() +")"; 1407 e_ino->ea_detach(); 1408 } 1409 } 1410 1411 // checking FSA if any 1412 if(e_ino != nullptr && e_ino->fsa_get_saved_status() == cat_inode::fsa_full) 1413 { 1414 if(perimeter == "") 1415 perimeter = "FSA"; 1416 else 1417 perimeter += " + FSA"; 1418 if(!empty) 1419 { 1420 const filesystem_specific_attribute_list *tmp = e_ino->get_fsa(); 1421 if(tmp == nullptr) 1422 throw SRC_BUG; 1423 perimeter += "(" + deci(tmp->size()).human() + ")"; 1424 e_ino->fsa_detach(); 1425 } 1426 } 1427 1428 if(e_dir == nullptr || !cat.read_second_time_dir()) 1429 st.incr_treated(); 1430 1431 if(e_mir != nullptr) 1432 e_mir->set_inode_wrote(true); 1433 1434 // still no exception raised, this all is fine 1435 if(display_treated) 1436 dialog.warning(string(gettext("OK ")) + juillet.get_string() + " " + perimeter); 1437 } 1438 else // excluded by filter 1439 { 1440 if(display_skipped) 1441 dialog.warning(string(gettext(SKIPPED)) + juillet.get_string()); 1442 1443 if(e_dir != nullptr) 1444 { 1445 juillet.enfile(&tmp_eod); 1446 cat.skip_read_to_parent_dir(); 1447 } 1448 if(e_dir == nullptr || !cat.read_second_time_dir()) 1449 st.incr_skipped(); 1450 } 1451 } 1452 } 1453 catch(Euser_abort & e) 1454 { 1455 throw; 1456 } 1457 catch(Ebug & e) 1458 { 1459 throw; 1460 } 1461 catch(Escript & e) 1462 { 1463 throw; 1464 } 1465 catch(Ethread_cancel & e) 1466 { 1467 throw; 1468 } 1469 catch(Egeneric & e) 1470 { 1471 dialog.warning(string(gettext("ERR ")) + juillet.get_string() + " : " + e.get_message()); 1472 if(e_dir == nullptr || !cat.read_second_time_dir()) 1473 st.incr_errored(); 1474 } 1475 } 1476 } 1477 filtre_merge(user_interaction & dialog,memory_pool * pool,const mask & filtre,const mask & subtree,const pile_descriptor & pdesc,catalogue & cat,const catalogue * ref1,const catalogue * ref2,bool info_details,bool display_treated,bool display_treated_only_dir,bool display_skipped,statistics & st,bool make_empty_dir,const mask & ea_mask,const mask & compr_mask,const infinint & min_compr_size,bool keep_compressed,const crit_action & over_action,bool warn_overwrite,bool decremental_mode,const infinint & sparse_file_min_size,const fsa_scope & scope)1478 void filtre_merge(user_interaction & dialog, 1479 memory_pool *pool, 1480 const mask & filtre, 1481 const mask & subtree, 1482 const pile_descriptor & pdesc, 1483 catalogue & cat, 1484 const catalogue * ref1, 1485 const catalogue * ref2, 1486 bool info_details, 1487 bool display_treated, 1488 bool display_treated_only_dir, 1489 bool display_skipped, 1490 statistics & st, 1491 bool make_empty_dir, 1492 const mask & ea_mask, 1493 const mask & compr_mask, 1494 const infinint & min_compr_size, 1495 bool keep_compressed, 1496 const crit_action & over_action, 1497 bool warn_overwrite, 1498 bool decremental_mode, 1499 const infinint & sparse_file_min_size, 1500 const fsa_scope & scope) 1501 { 1502 compression stock_algo; 1503 1504 if(display_treated_only_dir && display_treated) 1505 display_treated = false; 1506 // avoiding the report of action performed for each entry 1507 // specific code in this function will show instead the current directory 1508 // under which file are processed 1509 else 1510 display_treated_only_dir = false; // avoid incoherence 1511 1512 stock_algo = pdesc.compr->get_algo(); 1513 thread_cancellation thr_cancel; 1514 const cat_eod tmp_eod; 1515 const catalogue *ref_tab[] = { ref1, ref2, nullptr }; 1516 infinint etiquette_offset = 0; 1517 map <infinint, cat_etoile *> corres_copy; 1518 const cat_entree *e = nullptr; 1519 U_I index = 0; 1520 defile juillet = FAKE_ROOT; 1521 infinint fake_repeat = 0; 1522 bool abort = false; 1523 crit_action *decr = nullptr; // will point to a locally allocated crit_action 1524 // for decremental backup (argument overwrite is ignored) 1525 const crit_action *overwrite = &over_action; // may point to &decr if 1526 // decremental backup is asked 1527 1528 // STEP 0: Getting ready 1529 1530 st.clear(); 1531 1532 if(decremental_mode) 1533 { 1534 if(ref1 == nullptr || ref2 == nullptr) 1535 { 1536 dialog.pause(gettext("Decremental mode is useless when merging is not applied to both an archive of reference and an auxiliary archive of reference. Ignore decremental mode and continue?")); 1537 decremental_mode = false; 1538 } 1539 else 1540 { 1541 // allocating decr to "{T&R&~R&(A|!H)}[S*] P* ; {(e&~e&r&~r)|(!e&!~e)}[*s] *p" 1542 // 1543 // which means to record just data presence (S*) when: 1544 // both entries are of the same type (T) 1545 // and both have the same modification date (R&~R) 1546 // and this is the first time we meet this hard linked inode, or this is not a hard linked inode (A|!H) 1547 // else data is taken as is (P*) from the "in place" archive 1548 // EA and FSA are recorded as present when: 1549 // both entries have EA/FSA present (e&~e) 1550 // and both EA/FSA set have the same date (r&~r) 1551 // OR 1552 // none entry has EA/FSA present 1553 // else the EA/FSA (or the absence of EA/FSA) is taken from the "in place" archive 1554 1555 try 1556 { 1557 crit_chain *decr_crit_chain = new (pool) crit_chain(); 1558 if(decr_crit_chain == nullptr) 1559 throw Ememory("filtre_merge"); 1560 decr = decr_crit_chain; 1561 crit_and c_and = crit_and(); 1562 crit_or c_or = crit_or(); 1563 1564 c_and.clear(); 1565 c_or.clear(); 1566 c_and.add_crit(crit_same_type()); 1567 c_and.add_crit(crit_in_place_data_more_recent()); 1568 c_and.add_crit(crit_invert(crit_in_place_data_more_recent())); 1569 c_or.add_crit(crit_in_place_is_new_hardlinked_inode()); 1570 c_or.add_crit(crit_not(crit_in_place_is_hardlinked_inode())); 1571 c_and.add_crit(c_or); 1572 1573 decr_crit_chain->add(testing( 1574 c_and, 1575 crit_constant_action(data_preserve_mark_already_saved, EA_undefined), 1576 crit_constant_action(data_preserve, EA_undefined) 1577 )); 1578 1579 c_and.clear(); 1580 c_or.clear(); 1581 c_and.add_crit(crit_in_place_EA_present()); 1582 c_and.add_crit(crit_invert(crit_in_place_EA_present())); 1583 c_and.add_crit(crit_in_place_EA_more_recent()); 1584 c_and.add_crit(crit_invert(crit_in_place_EA_more_recent())); 1585 c_or.add_crit(c_and); 1586 c_and.clear(); 1587 c_and.add_crit(crit_not(crit_in_place_EA_present())); 1588 c_and.add_crit(crit_not(crit_invert(crit_in_place_EA_present()))); 1589 c_or.add_crit(c_and); 1590 1591 decr_crit_chain->add(testing( 1592 c_or, 1593 crit_constant_action(data_undefined, EA_preserve_mark_already_saved), 1594 crit_constant_action(data_undefined, EA_preserve) 1595 )); 1596 } 1597 catch(...) 1598 { 1599 if(decr != nullptr) 1600 { 1601 delete decr; 1602 decr = nullptr; 1603 } 1604 throw; 1605 } 1606 overwrite = decr; 1607 } 1608 } 1609 1610 /// End of Step 0 1611 1612 try 1613 { 1614 1615 if(overwrite == nullptr) 1616 throw SRC_BUG; 1617 1618 1619 // STEP 1: 1620 // we merge catalogues (pointed to by ref_tab[]) of each archive and produce a resulting catalogue 'cat' 1621 // the merging resolves overwriting conflicts following the "criterium" rule 1622 // each object of the catalogue is cloned and stored in 'cat', these clones get dump to archive at step 2 1623 1624 try 1625 { 1626 1627 while(ref_tab[index] != nullptr) // for each catalogue of reference (ref. and eventually auxiliary ref.) do: 1628 { 1629 juillet = FAKE_ROOT; 1630 cat.reset_add(); 1631 cat.reset_read(); 1632 ref_tab[index]->reset_read(); 1633 1634 if(info_details) 1635 { 1636 const char *ptr; 1637 switch(index) 1638 { 1639 case 0: 1640 ptr = gettext("first"); 1641 break; 1642 case 1: 1643 ptr = gettext("second"); 1644 break; 1645 default: 1646 ptr = gettext("next"); // not yet used, but room is made for future evolutions 1647 break; 1648 } 1649 dialog.printf(gettext("Merging/filtering files from the %s archive..."), ptr); 1650 } 1651 1652 while(ref_tab[index]->read(e)) // examining the content of the current archive of reference, each entry one by one 1653 { 1654 const cat_nomme *e_nom = dynamic_cast<const cat_nomme *>(e); 1655 const cat_mirage *e_mir = dynamic_cast<const cat_mirage *>(e); 1656 const cat_directory *e_dir = dynamic_cast<const cat_directory *>(e); 1657 const cat_detruit *e_detruit = dynamic_cast<const cat_detruit *>(e); 1658 1659 juillet.enfile(e); 1660 thr_cancel.check_self_cancellation(); 1661 if(e_nom != nullptr) 1662 { 1663 try 1664 { 1665 if(subtree.is_covered(juillet.get_path()) && (e_dir != nullptr || filtre.is_covered(e_nom->get_name()))) 1666 { 1667 cat_entree *dolly = make_clone(e_nom, pool, corres_copy, etiquette_offset); 1668 1669 // now that we have a clone object we must add the copied object to the catalogue, respecting the overwriting constaints 1670 1671 try 1672 { 1673 string the_name = e_nom->get_name(); 1674 const cat_nomme *already_here = nullptr; // may receive an address when an object of that name already exists in the resultant catalogue 1675 1676 // some different types of pointer to the "dolly" object 1677 1678 cat_nomme *dolly_nom = dynamic_cast<cat_nomme *>(dolly); 1679 cat_directory *dolly_dir = dynamic_cast<cat_directory *>(dolly); 1680 cat_mirage *dolly_mir = dynamic_cast<cat_mirage *>(dolly); 1681 cat_inode *dolly_ino = dynamic_cast<cat_inode *>(dolly); 1682 1683 if(dolly_mir != nullptr) 1684 dolly_ino = dolly_mir->get_inode(); 1685 1686 if(cat.read_if_present(& the_name, already_here)) // An entry of that name already exists in the resulting catalogue 1687 { 1688 1689 // some different types of pointer to the "already_here" object (aka 'inplace" object) 1690 const cat_mirage *al_mir = dynamic_cast<const cat_mirage *>(already_here); 1691 const cat_detruit *al_det = dynamic_cast<const cat_detruit *>(already_here); 1692 const cat_ignored *al_ign = dynamic_cast<const cat_ignored *>(already_here); 1693 const cat_ignored_dir *al_igndir = dynamic_cast<const cat_ignored_dir *>(already_here); 1694 const cat_inode *al_ino = dynamic_cast<const cat_inode *>(already_here); 1695 const cat_directory *al_dir = dynamic_cast<const cat_directory *>(already_here); 1696 const string full_name = juillet.get_string(); 1697 1698 over_action_data act_data; 1699 over_action_ea act_ea; 1700 1701 if(al_mir != nullptr) 1702 al_ino = al_mir->get_inode(); 1703 1704 if(dolly_nom == nullptr) 1705 throw SRC_BUG; // dolly should be the clone of a cat_nomme object which is not the case here 1706 1707 // evaluating the overwriting policy 1708 1709 overwrite->get_action(*already_here, *dolly_nom, act_data, act_ea); 1710 1711 if(act_data == data_ask) 1712 act_data = crit_ask_user_for_data_action(dialog, full_name, already_here, dolly); 1713 1714 // possibly modifying the resulting action when warning is requested 1715 1716 if(warn_overwrite) 1717 { 1718 switch(act_data) 1719 { 1720 case data_overwrite: 1721 case data_overwrite_mark_already_saved: 1722 case data_remove: 1723 case data_preserve_mark_already_saved: 1724 try 1725 { 1726 string action; 1727 1728 switch(act_data) 1729 { 1730 case data_overwrite: 1731 action = gettext("overwritten"); 1732 break; 1733 case data_overwrite_mark_already_saved: 1734 case data_preserve_mark_already_saved: 1735 action = gettext("dropped from the archive and marked as already saved"); 1736 break; 1737 case data_remove: 1738 action = gettext("removed"); 1739 break; 1740 default: 1741 throw SRC_BUG; 1742 } 1743 dialog.pause(tools_printf(gettext("Data of file %S is about to be %S, proceed?"), &full_name, &action)); 1744 } 1745 catch(Euser_abort & e) 1746 { 1747 act_data = data_preserve; 1748 } 1749 break; 1750 case data_preserve: 1751 case data_undefined: 1752 case data_ask: 1753 break; 1754 default: 1755 throw SRC_BUG; 1756 } 1757 1758 switch(act_ea) 1759 { 1760 case EA_overwrite: 1761 case EA_clear: 1762 case EA_preserve_mark_already_saved: 1763 case EA_overwrite_mark_already_saved: 1764 case EA_merge_overwrite: 1765 try 1766 { 1767 string action; 1768 1769 switch(act_ea) 1770 { 1771 case EA_overwrite: 1772 action = gettext("replaced"); 1773 break; 1774 case EA_clear: 1775 action = gettext("removed from the archive"); 1776 break; 1777 case EA_preserve_mark_already_saved: 1778 case EA_overwrite_mark_already_saved: 1779 action = gettext("dropped from the archive and marked as already saved"); 1780 break; 1781 case EA_merge_overwrite: 1782 action = gettext("merged with possible overwriting"); 1783 break; 1784 default: 1785 throw SRC_BUG; 1786 } 1787 dialog.pause(tools_printf(gettext("EA and FSA of file %S are about to be %S, proceed?"), &full_name, &action)); 1788 } 1789 catch(Euser_abort & e) 1790 { 1791 act_data = data_preserve; 1792 } 1793 1794 break; 1795 case EA_preserve: 1796 case EA_merge_preserve: 1797 case EA_ask: 1798 case EA_undefined: 1799 break; 1800 default: 1801 throw SRC_BUG; 1802 } 1803 } 1804 1805 1806 1807 1808 switch(act_data) 1809 { 1810 /////////////////////////// FIRST ACTIONS CATEGORY for DATA ///// 1811 case data_preserve: 1812 case data_preserve_mark_already_saved: 1813 case data_undefined: // remaining data_undefined at the end of the evaluation defaults to data_preserve 1814 1815 // drop data if necessary 1816 1817 if(act_data == data_preserve_mark_already_saved 1818 && al_ino != nullptr) 1819 { 1820 cat_inode *tmp = const_cast<cat_inode *>(al_ino); 1821 if(tmp->get_saved_status() == s_saved) 1822 tmp->set_saved_status(s_not_saved); // dropping data 1823 } 1824 1825 // EA consideration 1826 1827 if(act_ea == EA_ask) 1828 { 1829 if(dolly_ino != nullptr && al_ino != nullptr 1830 && (dolly_ino->ea_get_saved_status() != cat_inode::ea_none 1831 || al_ino->ea_get_saved_status() != cat_inode::ea_none 1832 || dolly_ino->fsa_get_saved_status() != cat_inode::fsa_none 1833 || al_ino->fsa_get_saved_status() != cat_inode::fsa_none) 1834 ) 1835 act_ea = crit_ask_user_for_EA_action(dialog, full_name, already_here, dolly); 1836 else 1837 act_ea = EA_preserve; // whatever what we want is, as no EA exist for both inplace and to be added objects, there is just no need to ask for that. 1838 } 1839 1840 switch(act_ea) 1841 { 1842 case EA_preserve: 1843 case EA_undefined: // remaining ea_undefined at the end of the evaluation defaults to ea_preserve 1844 // nothing to do 1845 break; 1846 case EA_overwrite: 1847 case EA_overwrite_mark_already_saved: 1848 case EA_merge_preserve: 1849 case EA_merge_overwrite: 1850 if(display_treated) 1851 dialog.warning(tools_printf(gettext("EA and FSA of file %S from first archive have been updated with those of same named file of the auxiliary archive"), &full_name)); 1852 do_EFSA_transfert(dialog, pool, act_ea, const_cast<cat_inode *>(al_ino), dolly_ino); 1853 break; 1854 1855 case EA_preserve_mark_already_saved: 1856 1857 if(al_ino != nullptr && al_ino->ea_get_saved_status() == cat_inode::ea_full) 1858 { 1859 const_cast<cat_inode *>(al_ino)->ea_set_saved_status(cat_inode::ea_partial); 1860 if(display_treated) 1861 dialog.warning(tools_printf(gettext("EA of file %S from first archive have been dropped and marked as already saved"), &full_name)); 1862 } 1863 if(al_ino != nullptr && al_ino->fsa_get_saved_status() == cat_inode::fsa_full) 1864 { 1865 const_cast<cat_inode *>(al_ino)->fsa_set_saved_status(cat_inode::fsa_partial); 1866 if(display_treated) 1867 dialog.warning(tools_printf(gettext("FSA of file %S from first archive have been dropped and marked as already saved"), &full_name)); 1868 } 1869 break; 1870 1871 case EA_clear: 1872 if(al_ino != nullptr && al_ino->ea_get_saved_status() != cat_inode::ea_none) 1873 { 1874 if(al_ino->ea_get_saved_status() == cat_inode::ea_full) 1875 st.decr_ea_treated(); 1876 if(display_treated) 1877 dialog.warning(tools_printf(gettext("EA of file %S from first archive have been removed"), &full_name)); 1878 const_cast<cat_inode *>(al_ino)->ea_set_saved_status(cat_inode::ea_none); 1879 } 1880 if(al_ino != nullptr && al_ino->fsa_get_saved_status() != cat_inode::fsa_none) 1881 { 1882 if(al_ino->fsa_get_saved_status() == cat_inode::fsa_full) 1883 st.decr_fsa_treated(); 1884 if(display_treated) 1885 dialog.warning(tools_printf(gettext("FSA of file %S from first archive have been removed"), &full_name)); 1886 const_cast<cat_inode *>(al_ino)->fsa_set_saved_status(cat_inode::fsa_none); 1887 } 1888 1889 break; 1890 1891 default: 1892 throw SRC_BUG; 1893 } 1894 1895 1896 // we must keep the existing entry in the catalogue 1897 1898 if(display_skipped && (dolly_dir == nullptr || al_dir == nullptr)) 1899 dialog.warning(tools_printf(gettext("Data of file %S from first archive has been preserved from overwriting"), &full_name)); 1900 1901 if(al_dir != nullptr && dolly_dir != nullptr) 1902 { 1903 // we can recurse in the directory in both ref and current catalogue because both entries are directories 1904 try 1905 { 1906 cat.re_add_in(al_dir->get_name()); // trying to update the add cursor of cat 1907 } 1908 catch(Erange & e) 1909 { 1910 ref_tab[index]->skip_read_to_parent_dir(); // updates back the read cursor of catalogue of reference 1911 juillet.enfile(&tmp_eod); // updates back the read cursor of juillet 1912 cat.skip_read_to_parent_dir(); // updates back the read cursor of cat 1913 throw; 1914 } 1915 } 1916 else // there is not the possibility to recurse in directory in both reference catalogue and catalogue under construction 1917 { 1918 if(al_dir != nullptr) 1919 cat.skip_read_to_parent_dir(); 1920 if(dolly_dir != nullptr) 1921 { 1922 ref_tab[index]->skip_read_to_parent_dir(); 1923 juillet.enfile(&tmp_eod); 1924 } 1925 } 1926 1927 // we may need to clean the corres_copy map 1928 1929 if(dolly_mir != nullptr) 1930 clean_hard_link_base_from(dolly_mir, corres_copy); 1931 1932 // now we can safely destroy the clone object 1933 1934 delete dolly; 1935 dolly = nullptr; 1936 break; 1937 1938 /////////////////////////// SECOND ACTIONS CATEGORY for DATA ///// 1939 case data_overwrite: 1940 case data_overwrite_mark_already_saved: 1941 case data_remove: 1942 1943 // drop data if necessary 1944 1945 if(display_treated) 1946 { 1947 switch(act_data) 1948 { 1949 case data_remove: 1950 dialog.warning(tools_printf(gettext("Data of file %S taken from the first archive of reference has been removed"), &full_name)); 1951 break; 1952 default: 1953 dialog.warning(tools_printf(gettext("Data of file %S taken from the first archive of reference has been overwritten"), &full_name)); 1954 } 1955 } 1956 1957 if(act_data == data_overwrite_mark_already_saved && dolly_ino != nullptr) 1958 { 1959 if(dolly_ino->get_saved_status() == s_saved) 1960 dolly_ino->set_saved_status(s_not_saved); // dropping data 1961 } 1962 1963 // EA consideration 1964 1965 if(act_ea == EA_ask && act_data != data_remove) 1966 { 1967 if(dolly_ino != nullptr && al_ino != nullptr && 1968 (dolly_ino->ea_get_saved_status() != cat_inode::ea_none 1969 || al_ino->ea_get_saved_status() != cat_inode::ea_none 1970 || dolly_ino->fsa_get_saved_status() != cat_inode::fsa_none 1971 || al_ino->fsa_get_saved_status() != cat_inode::fsa_none)) 1972 act_ea = crit_ask_user_for_EA_action(dialog, full_name, already_here, dolly); 1973 else 1974 act_ea = EA_overwrite; // no need to ask here neither as both entries have no EA. 1975 } 1976 1977 if(act_data != data_remove) 1978 { 1979 switch(act_ea) 1980 { 1981 case EA_preserve: 1982 case EA_undefined: // remaining ea_undefined at the end of the evaluation defaults to ea_preserve 1983 do_EFSA_transfert(dialog, pool, EA_overwrite, dolly_ino, al_ino); 1984 break; 1985 case EA_overwrite: 1986 if(display_treated) 1987 dialog.warning(tools_printf(gettext("EA of file %S has been overwritten"), &full_name)); 1988 break; // nothing to do 1989 case EA_overwrite_mark_already_saved: 1990 if(display_treated) 1991 dialog.warning(tools_printf(gettext("EA of file %S has been overwritten and marked as already saved"), &full_name)); 1992 if(dolly_ino != nullptr && dolly_ino->ea_get_saved_status() == cat_inode::ea_full) 1993 dolly_ino->ea_set_saved_status(cat_inode::ea_partial); 1994 break; 1995 case EA_merge_preserve: 1996 if(display_treated) 1997 dialog.warning(tools_printf(gettext("EA of file %S from first archive have been updated with those of the same named file of the auxiliary archive"), &full_name)); 1998 do_EFSA_transfert(dialog, pool, EA_merge_overwrite, dolly_ino, al_ino); 1999 break; 2000 case EA_merge_overwrite: 2001 if(display_treated) 2002 dialog.warning(tools_printf(gettext("EA of file %S from first archive have been updated with those of the same named file of the auxiliary archive"), &full_name)); 2003 do_EFSA_transfert(dialog, pool, EA_merge_preserve, dolly_ino, al_ino); 2004 break; 2005 case EA_preserve_mark_already_saved: 2006 if(display_treated) 2007 dialog.warning(tools_printf(gettext("EA of file %S has been overwritten and marked as already saved"), &full_name)); 2008 do_EFSA_transfert(dialog, pool, EA_overwrite_mark_already_saved, dolly_ino, al_ino); 2009 break; 2010 case EA_clear: 2011 if(al_ino->ea_get_saved_status() != cat_inode::ea_none) 2012 { 2013 if(display_treated) 2014 dialog.warning(tools_printf(gettext("EA of file %S from first archive have been removed"), &full_name)); 2015 dolly_ino->ea_set_saved_status(cat_inode::ea_none); 2016 } 2017 break; 2018 default: 2019 throw SRC_BUG; 2020 } 2021 } 2022 else // data_remove 2023 { 2024 // we remove both objects in overwriting conflict: here for now the dolly clone of the to be added 2025 2026 if(dolly_mir != nullptr) 2027 clean_hard_link_base_from(dolly_mir, corres_copy); 2028 2029 if(dolly_dir != nullptr) 2030 { 2031 juillet.enfile(&tmp_eod); 2032 ref_tab[index]->skip_read_to_parent_dir(); 2033 dolly_dir = nullptr; 2034 } 2035 2036 // now we can safely destroy the clone object 2037 2038 delete dolly; 2039 dolly = nullptr; 2040 } 2041 2042 2043 // we must remove the existing entry present in the catalogue to make room for the new entry to be added 2044 2045 if(dolly_dir == nullptr || al_dir == nullptr || act_data == data_remove) // one or both are not directory we effectively must remove the entry from the catalogue 2046 { 2047 cat_ignored_dir *if_removed = nullptr; 2048 2049 // to known which counter to decrement 2050 2051 st.decr_treated(); 2052 2053 if(al_ino != nullptr) 2054 if(al_ino->ea_get_saved_status() == cat_inode::ea_full) 2055 st.decr_ea_treated(); 2056 2057 // hard link specific actions 2058 2059 if(al_mir != nullptr) 2060 { 2061 // update back hard link counter 2062 st.decr_hard_links(); 2063 2064 // updating counter from pointed to inode 2065 2066 const cat_inode*al_ptr_ino = al_mir->get_inode(); 2067 if(al_ptr_ino == nullptr) 2068 throw SRC_BUG; 2069 else 2070 if(al_ptr_ino->ea_get_saved_status() == cat_inode::ea_full) 2071 st.decr_ea_treated(); 2072 2073 // cleaning the corres_copy map from the pointed to cat_etoile object reference if necessary 2074 clean_hard_link_base_from(al_mir, corres_copy); 2075 } 2076 2077 2078 if(al_det != nullptr) 2079 st.decr_deleted(); 2080 2081 if(al_ign != nullptr || al_igndir != nullptr) 2082 st.decr_ignored(); 2083 2084 if(act_data == data_remove) 2085 st.incr_ignored(); 2086 2087 // remove the current entry from the catalogue 2088 if(al_dir != nullptr) 2089 { 2090 infinint tree_size = al_dir->get_tree_size(); 2091 map<infinint, infinint> tiquettes; 2092 map<infinint, infinint>::iterator ut; 2093 2094 st.add_to_ignored(tree_size); 2095 st.sub_from_treated(tree_size); 2096 st.sub_from_ea_treated(al_dir->get_tree_ea_num()); 2097 st.sub_from_hard_links(al_dir->get_tree_mirage_num()); 2098 2099 cat.skip_read_to_parent_dir(); 2100 2101 // updating corres_copy with hard_link that will be destroyed due to directory deletion 2102 tiquettes.clear(); 2103 al_dir->get_etiquettes_found_in_tree(tiquettes); 2104 ut = tiquettes.begin(); 2105 while(ut != tiquettes.end()) 2106 { 2107 map<infinint, cat_etoile *>::iterator it = corres_copy.find(ut->first); 2108 2109 if(it == corres_copy.end()) 2110 throw SRC_BUG; // unknown etiquettes found in directory tree 2111 2112 if(it->second->get_ref_count() < ut->second) 2113 throw SRC_BUG; 2114 // more reference found in directory tree toward this cat_etoile than 2115 // this cat_etoile is aware of ! 2116 2117 if(it->second->get_ref_count() == ut->second) 2118 // this cat_etoile will disapear because all its reference are located 2119 // in the directory tree we are about to remove, we must clean this 2120 // entry from corres_copy 2121 corres_copy.erase(it); 2122 2123 ++ut; 2124 } 2125 2126 2127 if(make_empty_dir) 2128 { 2129 if_removed = new (pool) cat_ignored_dir(*al_dir); 2130 if(if_removed == nullptr) 2131 throw Ememory("filtre_merge"); 2132 } 2133 } 2134 2135 try 2136 { 2137 2138 // we can now remove the entry from the catalogue 2139 cat.remove_read_entry(the_name); 2140 2141 // now that the ancient directory has been removed we can replace it by an cat_ignored_dir entry if required 2142 if(if_removed != nullptr) 2143 cat.add(if_removed); 2144 2145 } 2146 catch(...) 2147 { 2148 if(if_removed != nullptr) 2149 { 2150 delete if_removed; 2151 if_removed = nullptr; 2152 } 2153 throw; 2154 } 2155 2156 } 2157 else // both existing and the one to add are directories we can proceed to the merging of their contents 2158 { 2159 cat.re_add_in_replace(*dolly_dir); 2160 delete dolly; 2161 dolly = nullptr; 2162 } 2163 break; 2164 2165 default: /////////////////////////// THIRD ACTIONS CATEGORY for DATA ///// 2166 throw SRC_BUG; // unknown data action ! 2167 } 2168 2169 2170 } // end of overwiting considerations 2171 else 2172 if(index >= 1 && decremental_mode) 2173 { 2174 unsigned char firm; 2175 2176 // this entry only exists in the auxilliary archive of reference, we must thus replace it by a "cat_detruit 2177 // because it will have to be removed when restoring the decremental backup. 2178 2179 2180 // we may need to clean the corres_copy map 2181 if(dolly_mir != nullptr) 2182 { 2183 clean_hard_link_base_from(dolly_mir, corres_copy); 2184 dolly_mir = nullptr; 2185 } 2186 2187 // then taking care or directory hierarchy 2188 if(dolly_dir != nullptr) 2189 { 2190 juillet.enfile(&tmp_eod); 2191 ref_tab[index]->skip_read_to_parent_dir(); 2192 dolly_dir = nullptr; 2193 } 2194 2195 if(dolly != nullptr) 2196 { 2197 delete dolly; 2198 dolly = nullptr; 2199 } 2200 else 2201 throw SRC_BUG; 2202 dolly_ino = nullptr; 2203 2204 if(e_mir != nullptr) 2205 firm = e_mir->get_inode()->signature(); 2206 else 2207 firm = e->signature(); 2208 2209 dolly = new (pool) cat_detruit(the_name, firm, ref_tab[index]->get_current_reading_dir().get_last_modif()); 2210 if(dolly == nullptr) 2211 throw Ememory("filtre_merge"); 2212 dolly_nom = dynamic_cast<cat_nomme *>(dolly); 2213 } 2214 2215 if(dolly != nullptr) 2216 { 2217 const cat_inode *e_ino = dynamic_cast<const cat_inode *>(e); 2218 2219 cat.add(dolly); 2220 st.incr_treated(); 2221 2222 if(e_mir != nullptr) 2223 { 2224 st.incr_hard_links(); 2225 e_ino = e_mir->get_inode(); 2226 } 2227 2228 if(e_detruit != nullptr) 2229 st.incr_deleted(); 2230 2231 2232 if(e_ino != nullptr) 2233 { 2234 if(e_ino->ea_get_saved_status() == cat_inode::ea_full) 2235 st.incr_ea_treated(); 2236 2237 if(e_ino->fsa_get_saved_status() == cat_inode::fsa_full) 2238 st.incr_fsa_treated(); 2239 } 2240 2241 if(e_dir != nullptr) // we must chdir also for the *reading* method of this catalogue object 2242 { 2243 if(!cat.read_if_present(&the_name, already_here)) 2244 throw SRC_BUG; 2245 } 2246 } 2247 } 2248 catch(...) 2249 { 2250 if(dolly != nullptr) 2251 { 2252 delete dolly; 2253 dolly = nullptr; 2254 } 2255 throw; 2256 } 2257 } 2258 else // excluded by filters 2259 { 2260 if(e_dir != nullptr && make_empty_dir) 2261 { 2262 cat_ignored_dir *igndir = new (pool) cat_ignored_dir(*e_dir); 2263 if(igndir == nullptr) 2264 throw Ememory("filtre_merge"); 2265 else 2266 cat.add(igndir); 2267 } 2268 2269 if(display_skipped) 2270 dialog.warning(string(gettext(SKIPPED)) + juillet.get_string()); 2271 2272 st.incr_ignored(); 2273 if(e_dir != nullptr) 2274 { 2275 ref_tab[index]->skip_read_to_parent_dir(); 2276 juillet.enfile(&tmp_eod); 2277 } 2278 } 2279 } 2280 catch(Ebug & e) 2281 { 2282 throw; 2283 } 2284 catch(Euser_abort & e) 2285 { 2286 dialog.warning(juillet.get_string() + gettext(" not merged (user choice)")); 2287 2288 if(e_dir != nullptr) 2289 { 2290 dialog.warning(gettext("No file in this directory will be considered for merging.")); 2291 ref_tab[index]->skip_read_to_parent_dir(); 2292 juillet.enfile(&tmp_eod); 2293 } 2294 st.incr_errored(); 2295 } 2296 catch(Ethread_cancel & e) 2297 { 2298 throw; 2299 } 2300 catch(Escript & e) 2301 { 2302 throw; 2303 } 2304 catch(Elimitint & e) 2305 { 2306 throw; 2307 } 2308 catch(Egeneric & e) 2309 { 2310 dialog.warning(string(gettext("Error while considering file ")) + juillet.get_string() + " : " + e.get_message()); 2311 2312 if(e_dir != nullptr) 2313 { 2314 dialog.warning(string(gettext("Warning! No file in this directory will be considered for merging: ")) + juillet.get_string()); 2315 ref_tab[index]->skip_read_to_parent_dir(); 2316 juillet.enfile(&tmp_eod); 2317 } 2318 st.incr_errored(); 2319 } 2320 } 2321 else // cat_entree is not a cat_nomme object (this is a "cat_eod") 2322 { 2323 cat_entree *tmp = e->clone(); 2324 try 2325 { 2326 const cat_nomme *already = nullptr; 2327 2328 if(tmp == nullptr) 2329 throw Ememory("filtre_merge"); 2330 if(dynamic_cast<cat_eod *>(tmp) == nullptr) 2331 throw SRC_BUG; 2332 cat.add(tmp); // add cat_eod to catalogue (add cursor) 2333 cat.read_if_present(nullptr, already); // equivalent to cat_eod for the reading methods 2334 } 2335 catch(...) 2336 { 2337 if(tmp != nullptr) 2338 delete tmp; 2339 throw; 2340 } 2341 } 2342 } 2343 normalize_link_base(corres_copy); 2344 index++; // next archive 2345 etiquette_offset = corres_copy.size(); 2346 } 2347 } 2348 catch(Ethread_cancel & e) 2349 { 2350 abort = true; 2351 dialog.warning(gettext("File selection has been aborted. Now building the resulting archive with the already selected files")); 2352 if(e.immediate_cancel()) 2353 throw; 2354 } 2355 } 2356 catch(...) 2357 { 2358 if(decr != nullptr) 2359 { 2360 delete decr; 2361 decr = nullptr; 2362 overwrite = nullptr; 2363 } 2364 throw; 2365 } 2366 2367 if(decr != nullptr) 2368 { 2369 delete decr; 2370 decr = nullptr; 2371 overwrite = nullptr; 2372 } 2373 2374 // STEP 2: 2375 // 'cat' is now completed 2376 // we must copy the file's data, EA and FSA to the archive 2377 2378 2379 if(info_details) 2380 dialog.warning("Copying filtered files to the resulting archive..."); 2381 2382 cat.set_all_mirage_s_inode_wrote_field_to(false); 2383 cat.reset_read(); 2384 juillet = FAKE_ROOT; 2385 2386 try 2387 { 2388 thr_cancel.block_delayed_cancellation(true); 2389 2390 while(cat.read(e)) 2391 { 2392 cat_entree *e_var = const_cast<cat_entree *>(e); 2393 cat_nomme *e_nom = dynamic_cast<cat_nomme *>(e_var); 2394 cat_inode *e_ino = dynamic_cast<cat_inode *>(e_var); 2395 cat_file *e_file = dynamic_cast<cat_file *>(e_var); 2396 cat_mirage *e_mir = dynamic_cast<cat_mirage *>(e_var); 2397 cat_directory *e_dir = dynamic_cast<cat_directory *>(e_var); 2398 2399 if(e_var == nullptr) 2400 throw SRC_BUG; 2401 2402 cat_file::get_data_mode keep_mode = keep_compressed ? cat_file::keep_compressed : cat_file::keep_hole; 2403 2404 if(e_mir != nullptr) 2405 if(!e_mir->is_inode_wrote()) // we store only once the inode pointed by a set of hard links 2406 { 2407 e_ino = e_mir->get_inode(); 2408 e_file = dynamic_cast<cat_file *>(e_ino); 2409 } 2410 2411 juillet.enfile(e); 2412 thr_cancel.check_self_cancellation(); 2413 if(display_treated_only_dir) 2414 { 2415 if(e_dir != nullptr) 2416 dialog.warning(string(gettext("Inspecting directory ")) + juillet.get_string()); 2417 } 2418 2419 if(e_ino != nullptr) // inode 2420 { 2421 bool compute_file_crc = false; 2422 2423 if(e_file != nullptr) 2424 { 2425 const crc *val = nullptr; 2426 2427 if(!e_file->get_crc(val)) // this can occur when reading an old archive format 2428 compute_file_crc = true; 2429 } 2430 2431 // deciding whether the file will be compressed or not 2432 2433 if(e_file != nullptr) 2434 { 2435 if(keep_mode != cat_file::keep_compressed) 2436 if(compr_mask.is_covered(e_nom->get_name()) && e_file->get_size() >= min_compr_size) 2437 e_file->change_compression_algo_write(stock_algo); 2438 else 2439 e_file->change_compression_algo_write(none); 2440 else // keep compressed: 2441 e_file->change_compression_algo_write(e_file->get_compression_algo_read()); 2442 } 2443 2444 // deciding whether the sparse file detection mechanism will be enabled or not 2445 // the sparse file detection mechanism is faked active in keep_compressed mode 2446 // because we need to record that sparse file datastructure is used as compressed data 2447 2448 if(e_file != nullptr) 2449 { 2450 if(!sparse_file_min_size.is_zero() && keep_mode != cat_file::keep_compressed) // sparse_file detection is activated 2451 { 2452 if(e_file->get_size() > sparse_file_min_size) 2453 { 2454 e_file->set_sparse_file_detection_write(true); 2455 keep_mode = cat_file::normal; 2456 } 2457 else 2458 if(e_file->get_sparse_file_detection_read()) 2459 { 2460 e_file->set_sparse_file_detection_write(false); 2461 keep_mode = cat_file::normal; 2462 } 2463 } 2464 else // sparse file layer or absence of layer is unchanged 2465 e_file->set_sparse_file_detection_write(e_file->get_sparse_file_detection_read()); 2466 } 2467 2468 // saving inode's data 2469 2470 if(!save_inode(dialog, 2471 pool, 2472 juillet.get_string(), 2473 e_var, 2474 pdesc, 2475 info_details, 2476 display_treated, 2477 true, // alter_atime 2478 false, // check_change 2479 compute_file_crc, 2480 keep_mode, 2481 cat, 2482 0, // repeat_count 2483 0, // repeat_byte 2484 sparse_file_min_size, 2485 nullptr, // semaphore 2486 fake_repeat)) 2487 throw SRC_BUG; 2488 else // succeeded saving 2489 { 2490 if(e_mir != nullptr) 2491 e_mir->set_inode_wrote(true); 2492 } 2493 2494 // saving inode's EA 2495 2496 if(e_ino->ea_get_saved_status() == cat_inode::ea_full) 2497 { 2498 cat.pre_add_ea(e); 2499 // ignoring the return value of save_ea, exceptions may still propagate 2500 (void)save_ea(dialog, juillet.get_string(), e_ino, pdesc, display_treated); 2501 cat.pre_add_ea_crc(e); 2502 } 2503 2504 // saving inode's FSA 2505 if(e_ino->fsa_get_saved_status() == cat_inode::fsa_full) 2506 { 2507 cat.pre_add_fsa(e); 2508 // ignoring the return value of save_fsa, exceptions may still propagate 2509 (void)save_fsa(dialog, juillet.get_string(), e_ino, pdesc, display_treated); 2510 cat.pre_add_fsa_crc(e); 2511 } 2512 } 2513 else // not an inode 2514 { 2515 cat.pre_add(e); 2516 if(e_mir != nullptr && (e_mir->get_inode()->get_saved_status() == s_saved || e_mir->get_inode()->ea_get_saved_status() == cat_inode::ea_full)) 2517 if(display_treated) 2518 dialog.warning(string(gettext("Adding Hard link to archive: "))+juillet.get_string()); 2519 } 2520 2521 // we can now check for interrution requests 2522 thr_cancel.block_delayed_cancellation(false); 2523 thr_cancel.block_delayed_cancellation(true); 2524 } 2525 } 2526 catch(Ethread_cancel & e) 2527 { 2528 cat.tail_catalogue_to_current_read(); 2529 cat.change_location(pdesc); 2530 if(pdesc.compr->is_compression_suspended()) 2531 { 2532 pdesc.stack->sync_write_above(pdesc.compr); 2533 pdesc.compr->resume_compression(); 2534 } 2535 thr_cancel.block_delayed_cancellation(false); 2536 throw; 2537 } 2538 2539 cat.change_location(pdesc); 2540 if(pdesc.compr->is_compression_suspended()) 2541 { 2542 pdesc.stack->sync_write_above(pdesc.compr); 2543 pdesc.compr->resume_compression(); 2544 } 2545 thr_cancel.block_delayed_cancellation(false); 2546 2547 if(abort) 2548 throw Ethread_cancel(false, 0); 2549 } 2550 filtre_sequentially_read_all_catalogue(catalogue & cat,user_interaction & dialog,bool lax_read_mode)2551 void filtre_sequentially_read_all_catalogue(catalogue & cat, 2552 user_interaction & dialog, 2553 bool lax_read_mode) 2554 { 2555 const cat_entree *e; 2556 thread_cancellation thr_cancel; 2557 defile juillet = FAKE_ROOT; 2558 2559 cat.set_all_mirage_s_inode_wrote_field_to(false); 2560 cat.reset_read(); 2561 2562 try 2563 { 2564 while(cat.read(e)) 2565 { 2566 const cat_file *e_file = dynamic_cast<const cat_file *>(e); 2567 const cat_inode *e_ino = dynamic_cast<const cat_inode *>(e); 2568 const cat_mirage *e_mir = dynamic_cast<const cat_mirage *>(e); 2569 const crc *check = nullptr; 2570 2571 juillet.enfile(e); 2572 2573 thr_cancel.check_self_cancellation(); 2574 if(e_mir != nullptr) 2575 { 2576 if(!e_mir->is_inode_wrote()) 2577 { 2578 e_file = dynamic_cast<const cat_file *>(e_mir->get_inode()); 2579 e_ino = e_mir->get_inode(); 2580 } 2581 } 2582 2583 try 2584 { 2585 if(e_file != nullptr) 2586 (void)e_file->get_crc(check); 2587 } 2588 catch(Erange & e) 2589 { 2590 string msg = string(gettext("failed reading CRC from file: ")) + juillet.get_string(); 2591 if(lax_read_mode) 2592 dialog.warning(msg); 2593 else 2594 throw Edata(msg); 2595 } 2596 2597 if(e_mir != nullptr && (e_ino != nullptr || e_file != nullptr)) 2598 e_mir->set_inode_wrote(true); 2599 2600 try 2601 { 2602 if(e_ino != nullptr) 2603 { 2604 if(e_ino->ea_get_saved_status() == cat_inode::ea_full) 2605 { 2606 (void)e_ino->get_ea(); 2607 e_ino->ea_get_crc(check); 2608 } 2609 if(e_ino->fsa_get_saved_status() == cat_inode::fsa_full) 2610 { 2611 (void)e_ino->get_fsa(); 2612 e_ino->fsa_get_crc(check); 2613 } 2614 } 2615 } 2616 catch(Erange & e) 2617 { 2618 string msg = string(gettext("Failed reading CRC for EA: ")) + juillet.get_string(); 2619 2620 if(lax_read_mode) 2621 dialog.warning(msg); 2622 else 2623 throw Edata(msg); 2624 } 2625 } 2626 } 2627 catch(Erange & e) 2628 { 2629 dialog.warning(string(gettext("Error met while reading next entry: ")) + juillet.get_string()); 2630 } 2631 } 2632 2633 2634 save_inode(user_interaction & dialog,memory_pool * pool,const string & info_quoi,cat_entree * & e,const pile_descriptor & pdesc,bool info_details,bool display_treated,bool alter_atime,bool check_change,bool compute_crc,cat_file::get_data_mode keep_mode,const catalogue & cat,const infinint & repeat_count,const infinint & repeat_byte,const infinint & hole_size,semaphore * sem,infinint & current_wasted_bytes)2635 static bool save_inode(user_interaction & dialog, 2636 memory_pool *pool, 2637 const string & info_quoi, 2638 cat_entree * & e, 2639 const pile_descriptor & pdesc, 2640 bool info_details, 2641 bool display_treated, 2642 bool alter_atime, 2643 bool check_change, 2644 bool compute_crc, 2645 cat_file::get_data_mode keep_mode, 2646 const catalogue & cat, 2647 const infinint & repeat_count, 2648 const infinint & repeat_byte, 2649 const infinint & hole_size, 2650 semaphore *sem, 2651 infinint & current_wasted_bytes) 2652 { 2653 bool ret = true; 2654 infinint current_repeat_count = 0; 2655 infinint storage_size; 2656 bool loop; 2657 cat_mirage *mir = dynamic_cast<cat_mirage *>(e); 2658 cat_inode *ino = dynamic_cast<cat_inode *>(e); 2659 bool resave_uncompressed = false; 2660 infinint rewinder = pdesc.stack->get_position(); // we skip back here if data must be saved uncompressed 2661 2662 if(mir != nullptr) 2663 { 2664 ino = mir->get_inode(); 2665 if(ino == nullptr) 2666 throw SRC_BUG; 2667 } 2668 2669 do // loop if resave_uncompressed is set, this is the OUTER LOOP 2670 { 2671 // PRE RECORDING THE INODE (for sequential reading) 2672 2673 cat.pre_add(e); 2674 2675 // TREATING SPECIAL CASES 2676 2677 if(ino == nullptr) 2678 return true; 2679 if(pdesc.compr == nullptr) 2680 throw SRC_BUG; 2681 if(ino->get_saved_status() != s_saved) 2682 { 2683 if(sem != nullptr) 2684 sem->raise(info_quoi, ino, false); 2685 return ret; 2686 } 2687 if(compute_crc && (keep_mode != cat_file::normal && keep_mode != cat_file::plain)) 2688 throw SRC_BUG; // cannot compute crc if data is compressed or hole datastructure not interpreted 2689 2690 // TREATNG INODE THAT NEED DATA SAVING 2691 2692 if(display_treated) 2693 { 2694 if(resave_uncompressed) 2695 dialog.warning(string(gettext("Resaving file without compression: ")) + info_quoi); 2696 else 2697 { 2698 string i_type = entree_to_string(ino); 2699 dialog.warning(tools_printf(gettext("Adding %S to archive: %S"), &i_type, &info_quoi)); 2700 } 2701 } 2702 2703 cat_file *fic = dynamic_cast<cat_file *>(ino); 2704 2705 if(fic != nullptr) 2706 { 2707 if(sem != nullptr) 2708 sem->raise(info_quoi, ino, true); 2709 2710 try 2711 { 2712 do // while "loop" is true, this is the INNER LOOP 2713 { 2714 loop = false; 2715 infinint start; 2716 generic_file *source = nullptr; 2717 2718 ////////////////////////////// 2719 // preparing the source 2720 2721 try 2722 { 2723 switch(keep_mode) 2724 { 2725 case cat_file::keep_compressed: 2726 if(fic->get_compression_algo_read() != fic->get_compression_algo_write()) 2727 keep_mode = cat_file::keep_hole; 2728 source = fic->get_data(keep_mode); 2729 break; 2730 case cat_file::keep_hole: 2731 source = fic->get_data(keep_mode); 2732 break; 2733 case cat_file::normal: 2734 if(fic->get_sparse_file_detection_read()) // source file already holds a sparse_file structure 2735 source = fic->get_data(cat_file::plain); // we must hide the holes for it can be redetected 2736 else 2737 source = fic->get_data(cat_file::normal); 2738 break; 2739 case cat_file::plain: 2740 throw SRC_BUG; // save_inode must never be called with this value 2741 default: 2742 throw SRC_BUG; // unknown value for keep_mode 2743 } 2744 } 2745 catch(...) 2746 { 2747 cat.pre_add_failed_mark(); 2748 throw; 2749 } 2750 2751 2752 2753 ////////////////////////////// 2754 // preparing the destination 2755 2756 2757 if(source != nullptr) 2758 { 2759 try 2760 { 2761 sparse_file *dst_hole = nullptr; 2762 infinint crc_size = tools_file_size_to_crc_size(fic->get_size()); 2763 crc * val = nullptr; 2764 const crc * original = nullptr; 2765 bool crc_available = false; 2766 2767 source->skip(0); 2768 source->read_ahead(0); 2769 2770 pdesc.stack->sync_write_above(pdesc.compr); 2771 pdesc.compr->sync_write(); // necessary in any case to reset the compression engine to be able at later time to decompress starting at that position 2772 if(keep_mode == cat_file::keep_compressed || fic->get_compression_algo_write() == none) 2773 pdesc.compr->suspend_compression(); 2774 else 2775 pdesc.compr->resume_compression(); 2776 2777 // now that compression has reset we can fetch the location where the data will be dropped: 2778 start = pdesc.stack->get_position(); 2779 fic->set_offset(start); 2780 2781 try 2782 { 2783 if(fic->get_sparse_file_detection_write() && keep_mode != cat_file::keep_compressed && keep_mode != cat_file::keep_hole) 2784 { 2785 // creating the sparse_file to copy data to destination 2786 2787 dst_hole = new (pool) sparse_file(pdesc.stack->top(), hole_size); 2788 if(dst_hole == nullptr) 2789 throw Ememory("save_inode"); 2790 pdesc.stack->push(dst_hole); 2791 } 2792 2793 ////////////////////////////// 2794 // proceeding to file's data backup 2795 2796 if(!compute_crc) 2797 crc_available = fic->get_crc(original); 2798 else 2799 crc_available = false; 2800 2801 source->copy_to(*pdesc.stack, crc_size, val); 2802 if(val == nullptr) 2803 throw SRC_BUG; 2804 2805 ////////////////////////////// 2806 // checking crc value and storing it in catalogue 2807 2808 if(compute_crc) 2809 fic->set_crc(*val); 2810 else 2811 { 2812 if(keep_mode == cat_file::normal && crc_available) 2813 { 2814 if(original == nullptr) 2815 throw SRC_BUG; 2816 if(typeid(*original) != typeid(*val)) 2817 throw SRC_BUG; 2818 if(*original != *val) 2819 throw Erange("save_inode", gettext("Copied data does not match CRC")); 2820 } 2821 } 2822 2823 ////////////////////////////// 2824 // checking whether saved files used sparse_file datastructure 2825 2826 if(dst_hole != nullptr) 2827 { 2828 pdesc.stack->sync_write_above(dst_hole); 2829 dst_hole->sync_write(); 2830 if(!dst_hole->has_seen_hole() && !dst_hole->has_escaped_data()) 2831 fic->set_sparse_file_detection_write(false); 2832 // here we drop the sparse_file datastructure as no hole 2833 // could be read. This will speed up extraction when used 2834 // normally (not with sequential reading, as the inode info 2835 // is already written to file and cannot be changed. 2836 // Reading as sparse_file datastructure a plain normal data 2837 // is possible while there is no data to escape, this is just 2838 // a bit more slower.). 2839 } 2840 2841 fichier_global *s_fic = dynamic_cast<fichier_global *>(source); 2842 if(s_fic != nullptr) 2843 s_fic->fadvise(fichier_global::advise_dontneed); 2844 source->terminate(); 2845 } 2846 catch(...) 2847 { 2848 if(val != nullptr) 2849 { 2850 delete val; 2851 val = nullptr; 2852 } 2853 2854 if(dst_hole != nullptr) 2855 { 2856 if(pdesc.stack->pop() != dst_hole) 2857 throw SRC_BUG; 2858 delete dst_hole; 2859 dst_hole = nullptr; 2860 } 2861 throw; 2862 } 2863 2864 if(val != nullptr) 2865 { 2866 delete val; 2867 val = nullptr; 2868 } 2869 2870 if(dst_hole != nullptr) 2871 { 2872 dst_hole->terminate(); 2873 if(pdesc.stack->pop() != dst_hole) 2874 throw SRC_BUG; 2875 delete dst_hole; 2876 dst_hole = nullptr; 2877 } 2878 2879 if(pdesc.stack->get_position() >= start) 2880 { 2881 storage_size = pdesc.stack->get_position() - start; 2882 fic->set_storage_size(storage_size); 2883 } 2884 else 2885 throw SRC_BUG; 2886 } 2887 catch(...) 2888 { 2889 delete source; 2890 source = nullptr; 2891 2892 // restore atime of source 2893 if(!alter_atime) 2894 tools_noexcept_make_date(info_quoi, false, ino->get_last_access(), ino->get_last_modif(), ino->get_last_modif()); 2895 throw; 2896 } 2897 delete source; 2898 source = nullptr; 2899 2900 ////////////////////////////// 2901 // adding the data CRC if escape marks are used 2902 2903 cat.pre_add_crc(ino); 2904 2905 ////////////////////////////// 2906 // checking if compressed data is smaller than uncompressed one 2907 2908 if(fic->get_size() <= storage_size 2909 && keep_mode != cat_file::keep_compressed 2910 && fic->get_compression_algo_write() != none) 2911 { 2912 infinint current_pos_tmp = pdesc.stack->get_position(); 2913 2914 if(current_pos_tmp <= rewinder) 2915 throw SRC_BUG; // we are positionned before the start of the current inode dump! 2916 if(pdesc.stack->skippable(generic_file::skip_backward, current_pos_tmp - rewinder)) 2917 { 2918 try 2919 { 2920 if(!pdesc.stack->skip(rewinder)) 2921 throw SRC_BUG; 2922 if(!resave_uncompressed) 2923 resave_uncompressed = true; 2924 else 2925 throw SRC_BUG; // should only be tried once per inode 2926 fic->change_compression_algo_write(none); 2927 break; // stop the inner loop 2928 } 2929 catch(Ebug & e) 2930 { 2931 throw; 2932 } 2933 catch(...) 2934 { 2935 if(info_details) 2936 dialog.warning(info_quoi + gettext(" : Failed resaving uncompressed the inode data")); 2937 // ignoring the error and continuing 2938 resave_uncompressed = false; 2939 if(pdesc.stack->get_position() != current_pos_tmp) 2940 throw SRC_BUG; 2941 } 2942 } 2943 else 2944 { 2945 if(info_details) 2946 dialog.warning(info_quoi + gettext(" : Resaving uncompressed the inode data to gain space is not possible, keeping data compressed")); 2947 } 2948 } 2949 else 2950 resave_uncompressed = false; 2951 2952 2953 ////////////////////////////// 2954 // checking for last_modification date change 2955 2956 if(check_change) 2957 { 2958 bool changed = false; 2959 2960 try 2961 { 2962 changed = fic->get_last_modif() != tools_get_mtime(dialog, 2963 info_quoi, 2964 true, 2965 true); // silently set to zero negative dates 2966 } 2967 catch(Erange & e) 2968 { 2969 dialog.warning(tools_printf(gettext("File has disappeared while we were reading it, cannot check whether it has changed during its backup: %S"), &info_quoi)); 2970 changed = false; 2971 } 2972 2973 if(changed) 2974 { 2975 if(current_repeat_count < repeat_count) 2976 { 2977 current_repeat_count++; 2978 infinint current_pos_tmp = pdesc.stack->get_position(); 2979 2980 try 2981 { 2982 if(pdesc.stack->skippable(generic_file::skip_backward, storage_size)) 2983 { 2984 if(!pdesc.stack->skip(start)) 2985 { 2986 if(!pdesc.stack->skip(current_repeat_count)) 2987 throw SRC_BUG; 2988 throw Erange("",""); // used locally 2989 } 2990 } 2991 else 2992 throw Erange("",""); // used locally, not propagated over this try / catch block 2993 } 2994 catch(...) 2995 { 2996 current_wasted_bytes += current_pos_tmp - start; 2997 if(pdesc.stack->get_position() != current_pos_tmp) 2998 throw SRC_BUG; 2999 } 3000 3001 if(repeat_byte.is_zero() || (current_wasted_bytes < repeat_byte)) 3002 { 3003 if(info_details) 3004 dialog.warning(tools_printf(gettext("WARNING! File modified while reading it for backup. Performing retry %i of %i"), ¤t_repeat_count, &repeat_count)); 3005 if(pdesc.stack->get_position() != start) 3006 cat.pre_add_waste_mark(); 3007 loop = true; 3008 3009 // updating the last modification date of file 3010 fic->set_last_modif(tools_get_mtime(dialog, 3011 info_quoi, 3012 true, 3013 true)); // silently set to zero negative date 3014 3015 // updating the size of the file 3016 fic->change_size(tools_get_size(info_quoi)); 3017 } 3018 else 3019 { 3020 dialog.warning(string(gettext("WARNING! File modified while reading it for backup. No more retry for that file to not exceed the wasted byte limit. File is ")) + info_quoi); 3021 fic->set_dirty(true); 3022 ret = false; 3023 } 3024 } 3025 else 3026 { 3027 dialog.warning(string(gettext("WARNING! File modified while reading it for backup, but no more retry allowed: ")) + info_quoi); 3028 fic->set_dirty(true); 3029 cat.pre_add_dirty(); // when in sequential reading 3030 ret = false; 3031 } 3032 } 3033 } 3034 3035 ////////////////////////////// 3036 // restore atime of source 3037 3038 if(!alter_atime) 3039 tools_noexcept_make_date(info_quoi, false, ino->get_last_access(), ino->get_last_modif(), ino->get_last_modif()); 3040 3041 } 3042 else 3043 throw SRC_BUG; // saved_status == s_saved, but no data available, and no exception raised; 3044 } 3045 while(loop); // INNER LOOP 3046 } 3047 catch(...) 3048 { 3049 if(sem != nullptr) 3050 sem->lower(); 3051 throw; 3052 } 3053 if(sem != nullptr) 3054 sem->lower(); 3055 } 3056 else // fic == nullptr 3057 if(sem != nullptr) 3058 { 3059 sem->raise(info_quoi, ino, true); 3060 sem->lower(); 3061 } 3062 } 3063 while(resave_uncompressed); // OUTER LOOP 3064 3065 return ret; 3066 } 3067 save_ea(user_interaction & dialog,const string & info_quoi,cat_inode * & ino,const pile_descriptor & pdesc,bool display_treated)3068 static bool save_ea(user_interaction & dialog, 3069 const string & info_quoi, 3070 cat_inode * & ino, 3071 const pile_descriptor & pdesc, 3072 bool display_treated) 3073 { 3074 bool ret = false; 3075 try 3076 { 3077 switch(ino->ea_get_saved_status()) 3078 { 3079 case cat_inode::ea_full: // if there is something to save 3080 if(ino->get_ea() != nullptr) 3081 { 3082 crc * val = nullptr; 3083 3084 try 3085 { 3086 if(display_treated) 3087 dialog.warning(string(gettext("Saving Extended Attributes for ")) + info_quoi); 3088 if(pdesc.compr->is_compression_suspended()) 3089 { 3090 pdesc.stack->sync_write_above(pdesc.compr); 3091 pdesc.compr->resume_compression(); // always compress EA (no size or filename consideration) 3092 } 3093 else 3094 { 3095 pdesc.stack->sync_write_above(pdesc.compr); 3096 pdesc.compr->sync_write(); // reset the compression engine to be able to decompress from that point later 3097 } 3098 3099 ino->ea_set_offset(pdesc.stack->get_position()); 3100 3101 3102 pdesc.stack->reset_crc(tools_file_size_to_crc_size(ino->ea_get_size())); // start computing CRC for any read/write on stack 3103 try 3104 { 3105 ino->get_ea()->dump(*pdesc.stack); 3106 } 3107 catch(...) 3108 { 3109 val = pdesc.stack->get_crc(); // this keeps "stack" in a coherent status 3110 throw; 3111 } 3112 val = pdesc.stack->get_crc(); 3113 ino->ea_set_crc(*val); 3114 ino->ea_detach(); 3115 ret = true; 3116 } 3117 catch(...) 3118 { 3119 if(val != nullptr) 3120 delete val; 3121 throw; 3122 } 3123 if(val != nullptr) 3124 delete val; 3125 } 3126 else 3127 throw SRC_BUG; 3128 break; 3129 case cat_inode::ea_partial: 3130 case cat_inode::ea_none: 3131 break; 3132 case cat_inode::ea_fake: 3133 throw SRC_BUG; //filesystem, must not provide inode in such a status 3134 case cat_inode::ea_removed: 3135 throw SRC_BUG; //filesystem, must not provide inode in such a status 3136 default: 3137 throw SRC_BUG; 3138 } 3139 } 3140 catch(Ebug & e) 3141 { 3142 throw; 3143 } 3144 catch(Euser_abort & e) 3145 { 3146 throw; 3147 } 3148 catch(Ethread_cancel & e) 3149 { 3150 throw; 3151 } 3152 catch(Egeneric & e) 3153 { 3154 dialog.warning(string(gettext("Error saving Extended Attributes for ")) + info_quoi + ": " + e.get_message()); 3155 } 3156 return ret; 3157 } 3158 3159 restore_atime(const string & chemin,const cat_inode * & ptr)3160 static void restore_atime(const string & chemin, const cat_inode * & ptr) 3161 { 3162 const cat_file * ptr_f = dynamic_cast<const cat_file *>(ptr); 3163 if(ptr_f != nullptr) 3164 tools_noexcept_make_date(chemin, false, ptr_f->get_last_access(), ptr_f->get_last_modif(), ptr_f->get_last_modif()); 3165 } 3166 save_fsa(user_interaction & dialog,const string & info_quoi,cat_inode * & ino,const pile_descriptor & pdesc,bool display_treated)3167 static bool save_fsa(user_interaction & dialog, 3168 const string & info_quoi, 3169 cat_inode * & ino, 3170 const pile_descriptor & pdesc, 3171 bool display_treated) 3172 { 3173 bool ret = false; 3174 try 3175 { 3176 switch(ino->fsa_get_saved_status()) 3177 { 3178 case cat_inode::fsa_full: // if there is something to save 3179 if(ino->get_fsa() != nullptr) 3180 { 3181 crc * val = nullptr; 3182 3183 try 3184 { 3185 if(display_treated) 3186 dialog.warning(string(gettext("Saving Filesystem Specific Attributes for ")) + info_quoi); 3187 if(pdesc.compr->get_algo() != none) 3188 { 3189 pdesc.stack->sync_write_above(pdesc.compr); 3190 pdesc.compr->suspend_compression(); // never compress EA (no size or filename consideration) 3191 } 3192 ino->fsa_set_offset(pdesc.stack->get_position()); 3193 3194 pdesc.stack->reset_crc(tools_file_size_to_crc_size(ino->fsa_get_size())); // start computing CRC for any read/write on stack 3195 try 3196 { 3197 ino->get_fsa()->write(*pdesc.stack); 3198 } 3199 catch(...) 3200 { 3201 val = pdesc.stack->get_crc(); // this keeps "" in a coherent status 3202 throw; 3203 } 3204 val = pdesc.stack->get_crc(); 3205 ino->fsa_set_crc(*val); 3206 ino->fsa_detach(); 3207 ret = true; 3208 3209 // compression is left suspended, save_inode, save_ea, will change or catalogue dump will change it if necessary 3210 } 3211 catch(...) 3212 { 3213 if(val != nullptr) 3214 delete val; 3215 throw; 3216 } 3217 if(val != nullptr) 3218 delete val; 3219 } 3220 else 3221 throw SRC_BUG; 3222 break; 3223 case cat_inode::fsa_partial: 3224 case cat_inode::fsa_none: 3225 break; 3226 default: 3227 throw SRC_BUG; 3228 } 3229 } 3230 catch(Ebug & e) 3231 { 3232 throw; 3233 } 3234 catch(Euser_abort & e) 3235 { 3236 throw; 3237 } 3238 catch(Ethread_cancel & e) 3239 { 3240 throw; 3241 } 3242 catch(Egeneric & e) 3243 { 3244 dialog.warning(string(gettext("Error saving Filesystem Specific Attributes for ")) + info_quoi + ": " + e.get_message()); 3245 } 3246 return ret; 3247 } 3248 do_EFSA_transfert(user_interaction & dialog,memory_pool * pool,over_action_ea action,cat_inode * place_ino,const cat_inode * add_ino)3249 static void do_EFSA_transfert(user_interaction &dialog, 3250 memory_pool *pool, 3251 over_action_ea action, 3252 cat_inode *place_ino, 3253 const cat_inode *add_ino) 3254 { 3255 ea_attributs *tmp_ea = nullptr; 3256 filesystem_specific_attribute_list *tmp_fsa = nullptr; 3257 3258 switch(action) 3259 { 3260 case EA_overwrite: 3261 case EA_overwrite_mark_already_saved: 3262 case EA_merge_preserve: 3263 case EA_merge_overwrite: 3264 break; 3265 case EA_preserve: 3266 case EA_preserve_mark_already_saved: 3267 case EA_clear: 3268 throw SRC_BUG; 3269 default: 3270 throw SRC_BUG; 3271 } 3272 3273 if(add_ino == nullptr) // to_add is not an inode thus cannot have any EA 3274 return; // we do nothing in any case as there is not different EA set in conflict 3275 3276 if(place_ino == nullptr) // in_place is not an inode thus cannot have any EA 3277 return; // nothing can be done neither here as the resulting object (in_place) cannot handle EA 3278 3279 // in the following we know that both in_place and to_add are inode, 3280 //we manipulate them thanks to their cat_inode * pointers (place_ino and add_ino) 3281 3282 switch(action) // action concerns both EA and FSA in spite of the name of its values 3283 { 3284 case EA_overwrite: 3285 3286 // overwriting last change date 3287 3288 if(add_ino->has_last_change()) 3289 place_ino->set_last_change(add_ino->get_last_change()); 3290 3291 // EA Consideration (for overwriting) 3292 3293 switch(add_ino->ea_get_saved_status()) 3294 { 3295 case cat_inode::ea_none: 3296 case cat_inode::ea_removed: 3297 place_ino->ea_set_saved_status(cat_inode::ea_none); 3298 break; 3299 case cat_inode::ea_partial: 3300 case cat_inode::ea_fake: 3301 place_ino->ea_set_saved_status(cat_inode::ea_partial); 3302 break; 3303 case cat_inode::ea_full: 3304 tmp_ea = new (pool) ea_attributs(*add_ino->get_ea()); // we clone the EA of add_ino 3305 if(tmp_ea == nullptr) 3306 throw Ememory("filtre::do_EFSA_transfert"); 3307 try 3308 { 3309 if(place_ino->ea_get_saved_status() == cat_inode::ea_full) // then we must drop the old EA: 3310 place_ino->ea_detach(); 3311 else 3312 place_ino->ea_set_saved_status(cat_inode::ea_full); 3313 place_ino->ea_attach(tmp_ea); 3314 tmp_ea = nullptr; 3315 } 3316 catch(...) 3317 { 3318 if(tmp_ea != nullptr) 3319 { 3320 delete tmp_ea; 3321 tmp_ea = nullptr; 3322 } 3323 throw; 3324 } 3325 break; 3326 default: 3327 throw SRC_BUG; 3328 } 3329 3330 // FSA Considerations (for overwriting) 3331 3332 switch(add_ino->fsa_get_saved_status()) 3333 { 3334 case cat_inode::fsa_none: 3335 place_ino->fsa_set_saved_status(cat_inode::fsa_none); 3336 break; 3337 case cat_inode::fsa_partial: 3338 place_ino->fsa_set_saved_status(cat_inode::fsa_partial); 3339 place_ino->fsa_partial_attach(add_ino->fsa_get_families()); 3340 break; 3341 case cat_inode::fsa_full: 3342 tmp_fsa = new (pool) filesystem_specific_attribute_list(*add_ino->get_fsa()); // we clone the FSA of add_ino 3343 if(tmp_fsa == nullptr) 3344 throw Ememory("filtre::do_EFSA_transfer"); 3345 try 3346 { 3347 if(place_ino->fsa_get_saved_status() == cat_inode::fsa_full) // we must drop the old FSA 3348 place_ino->fsa_detach(); 3349 else 3350 place_ino->fsa_set_saved_status(cat_inode::fsa_full); 3351 place_ino->fsa_attach(tmp_fsa); 3352 tmp_fsa = nullptr; 3353 } 3354 catch(...) 3355 { 3356 if(tmp_fsa != nullptr) 3357 { 3358 delete tmp_fsa; 3359 tmp_fsa = nullptr; 3360 } 3361 throw; 3362 } 3363 break; 3364 default: 3365 throw SRC_BUG; 3366 } 3367 break; // end of case EA_FSA_overwrite for action 3368 3369 case EA_overwrite_mark_already_saved: 3370 3371 // Overwriting Date 3372 3373 if(add_ino->has_last_change()) 3374 place_ino->set_last_change(add_ino->get_last_change()); 3375 3376 // EA considerations 3377 3378 place_ino->ea_set_saved_status(add_ino->ea_get_saved_status()); // at this step, ea_full may be set, it will be changed to ea_partial below. 3379 if(place_ino->ea_get_saved_status() == cat_inode::ea_full || place_ino->ea_get_saved_status() == cat_inode::ea_fake) 3380 place_ino->ea_set_saved_status(cat_inode::ea_partial); 3381 3382 // FSA considerations 3383 3384 place_ino->fsa_set_saved_status(add_ino->fsa_get_saved_status()); // at this step fsa_full may be set, will be changed to fsa_partial below 3385 if(place_ino->fsa_get_saved_status() == cat_inode::fsa_full) 3386 place_ino->fsa_set_saved_status(cat_inode::fsa_partial); 3387 3388 break; 3389 3390 case EA_merge_preserve: 3391 3392 // no last change date modification (preserve context) 3393 3394 // EA considerations 3395 3396 if(place_ino->ea_get_saved_status() == cat_inode::ea_full && add_ino->ea_get_saved_status() == cat_inode::ea_full) // we have something to merge 3397 { 3398 tmp_ea = new (pool) ea_attributs(); 3399 if(tmp_ea == nullptr) 3400 throw Ememory("filtre.cpp:do_EFSA_transfert"); 3401 try 3402 { 3403 merge_ea(*place_ino->get_ea(), *add_ino->get_ea(), *tmp_ea); 3404 place_ino->ea_detach(); 3405 place_ino->ea_attach(tmp_ea); 3406 tmp_ea = nullptr; 3407 } 3408 catch(...) 3409 { 3410 if(tmp_ea != nullptr) 3411 { 3412 delete tmp_ea; 3413 tmp_ea = nullptr; 3414 } 3415 throw; 3416 } 3417 } 3418 else 3419 if(add_ino->ea_get_saved_status() == cat_inode::ea_full) 3420 { 3421 place_ino->ea_set_saved_status(cat_inode::ea_full); // it was not the case else we would have executed the above block 3422 tmp_ea = new (pool) ea_attributs(*add_ino->get_ea()); // we clone the EA set of to_add 3423 if(tmp_ea == nullptr) 3424 throw Ememory("filtre.cpp:do_EFSA_transfert"); 3425 try 3426 { 3427 place_ino->ea_attach(tmp_ea); 3428 tmp_ea = nullptr; 3429 } 3430 catch(...) 3431 { 3432 if(tmp_ea != nullptr) 3433 { 3434 delete tmp_ea; 3435 tmp_ea = nullptr; 3436 } 3437 throw; 3438 } 3439 } 3440 // else nothing is done: either res_ino has full EA but ref_ino has not 3441 // or res_ino has not full EA nor do has ref_ino and nothing can be done neither 3442 3443 3444 // FSA considerations 3445 3446 if(place_ino->fsa_get_saved_status() == cat_inode::fsa_full && add_ino->fsa_get_saved_status() == cat_inode::fsa_full) // we have something to merge 3447 { 3448 tmp_fsa = new (pool) filesystem_specific_attribute_list(); 3449 if(tmp_fsa == nullptr) 3450 throw Ememory("filtre.cpp::do_EFSA_transfer"); 3451 3452 try 3453 { 3454 *tmp_fsa = *add_ino->get_fsa() + *place_ino->get_fsa(); // overwriting add_ino with place_ino's FSA 3455 place_ino->fsa_detach(); 3456 place_ino->fsa_attach(tmp_fsa); 3457 tmp_fsa = nullptr; 3458 } 3459 catch(...) 3460 { 3461 if(tmp_fsa != nullptr) 3462 { 3463 delete tmp_fsa; 3464 tmp_fsa = nullptr; 3465 } 3466 throw; 3467 } 3468 } 3469 else 3470 { 3471 if(add_ino->fsa_get_saved_status() == cat_inode::fsa_full) 3472 { 3473 place_ino->fsa_set_saved_status(cat_inode::fsa_full); 3474 tmp_fsa = new (pool) filesystem_specific_attribute_list(*add_ino->get_fsa()); 3475 if(tmp_fsa == nullptr) 3476 throw Ememory("filtre.cpp:do_EFSA_transfert"); 3477 try 3478 { 3479 place_ino->fsa_attach(tmp_fsa); 3480 tmp_fsa = nullptr; 3481 } 3482 catch(...) 3483 { 3484 if(tmp_fsa != nullptr) 3485 { 3486 delete tmp_fsa; 3487 tmp_fsa = nullptr; 3488 } 3489 throw; 3490 } 3491 } 3492 // else nothing to be done (in_place eventually has FSA and add_ino does nothing to add) 3493 } 3494 3495 break; 3496 3497 case EA_merge_overwrite: 3498 3499 // last change date transfert 3500 3501 if(add_ino->has_last_change()) 3502 place_ino->set_last_change(add_ino->get_last_change()); 3503 3504 // EA considerations 3505 3506 if(place_ino->ea_get_saved_status() == cat_inode::ea_full && add_ino->ea_get_saved_status() == cat_inode::ea_full) 3507 { 3508 tmp_ea = new (pool) ea_attributs(); 3509 if(tmp_ea == nullptr) 3510 throw Ememory("filtre.cpp:do_EFSA_transfert"); 3511 try 3512 { 3513 merge_ea(*add_ino->get_ea(), *place_ino->get_ea(), *tmp_ea); 3514 place_ino->ea_detach(); 3515 place_ino->ea_attach(tmp_ea); 3516 tmp_ea = nullptr; 3517 } 3518 catch(...) 3519 { 3520 if(tmp_ea != nullptr) 3521 { 3522 delete tmp_ea; 3523 tmp_ea = nullptr; 3524 } 3525 throw; 3526 } 3527 } 3528 else 3529 if(add_ino->ea_get_saved_status() == cat_inode::ea_full) 3530 { 3531 place_ino->ea_set_saved_status(cat_inode::ea_full); // it was not the case else we would have executed the above block 3532 tmp_ea = new (pool) ea_attributs(*add_ino->get_ea()); 3533 if(tmp_ea == nullptr) 3534 throw Ememory("filtre.cpp:do_EFSA_transfert"); 3535 try 3536 { 3537 place_ino->ea_attach(tmp_ea); 3538 tmp_ea = nullptr; 3539 } 3540 catch(...) 3541 { 3542 if(tmp_ea != nullptr) 3543 { 3544 delete tmp_ea; 3545 tmp_ea = nullptr; 3546 } 3547 throw; 3548 } 3549 } 3550 // else nothing is done: either res_ino has full EA but ref_ino has not 3551 // or res_ino has not full EA nor do has ref_ino and nothing can be done neither 3552 3553 // FSA considerations 3554 3555 if(place_ino->fsa_get_saved_status() == cat_inode::fsa_full && add_ino->fsa_get_saved_status() == cat_inode::fsa_full) // we have something to merge 3556 { 3557 tmp_fsa = new (pool) filesystem_specific_attribute_list(); 3558 if(tmp_fsa == nullptr) 3559 throw Ememory("filtre.cpp::do_EFSA_transfer"); 3560 3561 try 3562 { 3563 *tmp_fsa = *place_ino->get_fsa() + *add_ino->get_fsa(); // overwriting place_ino with add_ino's FSA 3564 place_ino->fsa_detach(); 3565 place_ino->fsa_attach(tmp_fsa); 3566 tmp_fsa = nullptr; 3567 } 3568 catch(...) 3569 { 3570 if(tmp_fsa != nullptr) 3571 { 3572 delete tmp_fsa; 3573 tmp_fsa = nullptr; 3574 } 3575 throw; 3576 } 3577 } 3578 else 3579 { 3580 if(add_ino->fsa_get_saved_status() == cat_inode::fsa_full) 3581 { 3582 place_ino->fsa_set_saved_status(cat_inode::fsa_full); 3583 tmp_fsa = new (pool) filesystem_specific_attribute_list(*add_ino->get_fsa()); 3584 if(tmp_fsa == nullptr) 3585 throw Ememory("filtre.cpp:do_EFSA_transfert"); 3586 try 3587 { 3588 place_ino->fsa_attach(tmp_fsa); 3589 tmp_fsa = nullptr; 3590 } 3591 catch(...) 3592 { 3593 if(tmp_fsa != nullptr) 3594 { 3595 delete tmp_fsa; 3596 tmp_fsa = nullptr; 3597 } 3598 throw; 3599 } 3600 } 3601 // else nothing to be done (in_place eventually has FSA and add_ino does nothing to add) 3602 } 3603 3604 3605 break; 3606 3607 default: 3608 throw SRC_BUG; 3609 } 3610 } 3611 merge_ea(const ea_attributs & ref1,const ea_attributs & ref2,ea_attributs & res)3612 static void merge_ea(const ea_attributs & ref1, const ea_attributs & ref2, ea_attributs &res) 3613 { 3614 string ent_key, ent_val; 3615 string val; 3616 3617 res = ref1; // assignment operator 3618 res.reset_read(); 3619 3620 ref2.reset_read(); 3621 while(ref2.read(ent_key, ent_val)) 3622 if(!res.find(ent_key, val)) 3623 res.add(ent_key, ent_val); 3624 } 3625 3626 make_clone(const cat_nomme * ref,memory_pool * pool,map<infinint,cat_etoile * > & hard_link_base,const infinint & etiquette_offset)3627 static cat_entree *make_clone(const cat_nomme *ref, 3628 memory_pool *pool, 3629 map<infinint, cat_etoile*> & hard_link_base, 3630 const infinint & etiquette_offset) 3631 { 3632 cat_entree *dolly = nullptr; // will be the address of the cloned object 3633 string the_name; 3634 const cat_mirage *ref_mir = dynamic_cast<const cat_mirage *>(ref); 3635 3636 if(ref == nullptr) 3637 throw SRC_BUG; 3638 3639 the_name = ref->get_name(); 3640 3641 3642 if(ref_mir != nullptr) // this is hard linked inode 3643 { 3644 // check whether this is the first time we see this file (in list of file covered by the file masks) 3645 map <infinint, cat_etoile *>::iterator it = hard_link_base.find(ref_mir->get_etiquette() + etiquette_offset); 3646 if(it == hard_link_base.end()) // this inode has not been yet recorded in the resulting archive 3647 { 3648 cat_etoile *filante = nullptr; 3649 dolly = ref_mir->get_inode()->clone(); // we must clone the attached inode 3650 try 3651 { 3652 cat_inode *dollinode = dynamic_cast<cat_inode *>(dolly); 3653 3654 if(dollinode == nullptr) 3655 throw Ememory("filtre:make_clone"); 3656 3657 infinint shift_etiquette = ref_mir->get_etiquette() + etiquette_offset; 3658 filante = new (pool) cat_etoile(dollinode, shift_etiquette); 3659 if(filante == nullptr) 3660 throw Ememory("make_clone"); 3661 try 3662 { 3663 dolly = nullptr; // the inode is now managed by filante 3664 dolly = new (pool) cat_mirage(the_name, filante); 3665 if(dolly == nullptr) 3666 throw Ememory("make_clone"); 3667 try 3668 { 3669 hard_link_base[shift_etiquette] = filante; // we now record this file_etiquette in the map of already enrolled hard_link sets 3670 } 3671 catch(...) 3672 { 3673 filante = nullptr; // now managed by the cat_mirage pointed to by dolly 3674 throw; 3675 } 3676 } 3677 catch(...) 3678 { 3679 if(filante != nullptr) 3680 { 3681 delete filante; 3682 filante = nullptr; 3683 } 3684 throw; 3685 } 3686 } 3687 catch(...) 3688 { 3689 if(dolly != nullptr) 3690 { 3691 delete dolly; 3692 dolly = nullptr; 3693 } 3694 throw; 3695 } 3696 } 3697 else // already added to archive 3698 dolly = new (pool) cat_mirage(the_name, it->second); // we make a new cat_mirage pointing to the cat_etoile already involved in the catalogue under construction 3699 } 3700 else // not a hard_link file 3701 dolly = ref->clone(); // we just clone the entry 3702 3703 if(dolly == nullptr) 3704 throw Ememory("make_clone"); 3705 3706 return dolly; 3707 } 3708 3709 clean_hard_link_base_from(const cat_mirage * mir,map<infinint,cat_etoile * > & hard_link_base)3710 static void clean_hard_link_base_from(const cat_mirage *mir, map<infinint, cat_etoile *> & hard_link_base) 3711 { 3712 if(mir->get_etoile_ref_count().is_zero()) 3713 throw SRC_BUG; // count should be >= 1 3714 3715 if(mir->get_etoile_ref_count() == 1) 3716 { 3717 map<infinint, cat_etoile *>::iterator it = hard_link_base.find(mir->get_etiquette()); 3718 const cat_inode *al_ptr_ino = mir->get_inode(); 3719 if(al_ptr_ino == nullptr) 3720 throw SRC_BUG; 3721 if(it == hard_link_base.end()) 3722 throw SRC_BUG; // the cat_etoile object pointed to by dolly_mir should be known by corres_copy 3723 hard_link_base.erase(it); 3724 } 3725 } 3726 normalize_link_base(map<infinint,cat_etoile * > & hard_link_base)3727 static void normalize_link_base(map<infinint, cat_etoile *> & hard_link_base) 3728 { 3729 infinint max_val = 0; 3730 map<infinint, cat_etoile *>::iterator it, ut; 3731 infinint num_etoile = hard_link_base.size(); 3732 infinint search = 0; 3733 3734 // first pass, looking highest etiquette number 3735 3736 for(it = hard_link_base.begin(); it != hard_link_base.end(); ++it) 3737 { 3738 if(it->first > max_val) 3739 max_val = it->first; 3740 } 3741 3742 // second pass, looking for holes and moving highest value to fill the gap in 3743 3744 while(search < num_etoile) 3745 { 3746 it = hard_link_base.find(search); 3747 if(it == hard_link_base.end()) 3748 { 3749 // unused etiquette value, looking for the higest value 3750 do 3751 { 3752 if(max_val <= search) 3753 throw SRC_BUG; // num_etoile > 0, but we cannot find a cat_etoile to move into the hole 3754 ut = hard_link_base.find(max_val); 3755 --max_val; 3756 } 3757 while(ut == hard_link_base.end()); 3758 3759 // we can now renumber cat_etoile tmp from max_val to search 3760 3761 cat_etoile *tmp = ut->second; 3762 if(tmp == nullptr) 3763 throw SRC_BUG; 3764 tmp->change_etiquette(search); 3765 hard_link_base.erase(ut); 3766 hard_link_base[search] = tmp; 3767 } 3768 ++search; 3769 } 3770 } 3771 make_overwriting_for_only_deleted(memory_pool * pool)3772 static const crit_action *make_overwriting_for_only_deleted(memory_pool *pool) 3773 { 3774 const crit_action *ret = new (pool) testing(crit_invert(crit_in_place_is_inode()), crit_constant_action(data_preserve, EA_preserve), crit_constant_action(data_overwrite, EA_overwrite)); 3775 if(ret == nullptr) 3776 throw Ememory("make_overwriting_fir_only_deleted"); 3777 3778 return ret; 3779 } 3780 3781 } // end of namespace 3782