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"), &current_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