dar-2.7.15/0000755000175000017500000000000014640025216007355 500000000000000dar-2.7.15/depcomp0000755000175000017500000005602014215102164010651 00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2021 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: dar-2.7.15/src/0000755000175000017500000000000014640025216010144 500000000000000dar-2.7.15/src/libdar/0000755000175000017500000000000014640025215011400 500000000000000dar-2.7.15/src/libdar/cat_etoile.hpp0000644000175000017500000000777614636066467014205 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_etoile.hpp /// \brief class holding an cat_inode object that get pointed by multiple mirage objects (smart pointers) to record hard links in a catalogue /// \ingroup Private #ifndef CAT_ETOILE_HPP #define CAT_ETOILE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include #include "cat_inode.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the hard link implementation /// etoile means star in French. See a star as a point from which are thrown many ray of light class cat_etoile { public: /// build an object ///\param[in] host is an inode, it must not be a directory (this would throw an Erange exception) ///\param[in] etiquette_number is the identifier of this multiply linked structure ///\note the given cat_inode is now managed by the cat_etoile object cat_etoile(cat_inode *host, const infinint & etiquette_number); cat_etoile(const cat_etoile & ref) = delete; // copy constructor not allowed for this class cat_etoile(cat_etoile && ref) = delete; cat_etoile & operator = (const cat_etoile & ref) = delete; // assignment not allowed for this class cat_etoile & operator = (cat_etoile && ref) = delete; ~cat_etoile() { delete hosted; }; void add_ref(void *ref); void drop_ref(void *ref); infinint get_ref_count() const { return refs.size(); }; cat_inode *get_inode() const { return hosted; }; infinint get_etiquette() const { return etiquette; }; void change_etiquette(const infinint & new_val) { etiquette = new_val; }; void disable_reduction_to_normal_inode() { tags.reduceable = 0; }; bool cannot_reduce_to_normal_inode() const { return tags.reduceable == 0; }; bool is_counted() const { return tags.counted; }; bool is_wrote() const { return tags.wrote; }; bool is_dumped() const { return tags.dumped; }; void set_counted(bool val) { tags.counted = val ? 1 : 0; }; void set_wrote(bool val) { tags.wrote = val ? 1 : 0; }; void set_dumped(bool val) { tags.dumped = val ? 1 : 0; }; // return the address of the first mirage that triggered the creation of this mirage // if this object is destroyed afterward this call returns nullptr const void *get_first_ref() const { if(refs.size() == 0) throw SRC_BUG; return refs.front(); }; private: /// bitfield used to record pointed to inode information struct bool_tags { unsigned counted : 1; ///< whether the inode has been counted unsigned wrote : 1; ///< whether the inode has its data copied to archive unsigned dumped : 1; ///< whether the inode information has been dumped in the catalogue unsigned reduceable : 1; ///< whether the inode can be reduce to normal inode unsigned : 4; ///< padding to get byte boundary and reserved for future use. bool_tags() { counted = wrote = dumped = 0; reduceable = 1; }; }; std::list refs; ///< list of pointers to the mirages objects, in the order of their creation cat_inode *hosted; infinint etiquette; bool_tags tags; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/op_tools.cpp0000644000175000017500000002675314636066467013722 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "op_tools.hpp" #include "nls_swap.hpp" #include "tools.hpp" #include "cat_all_entrees.hpp" using namespace std; namespace libdar { over_action_data op_tools_crit_ask_user_for_data_action(user_interaction & dialog, const string & full_name, const cat_entree *already_here, const cat_entree *dolly) { over_action_data ret = data_undefined; NLS_SWAP_IN; try { const string confirm = gettext("yes"); bool loop = true; string resp; while(loop) { dialog.printf(gettext("Conflict found while selecting the file to retain in the resulting archive:")); dialog.printf(gettext("User Decision requested for data of file %S"), &full_name); op_tools_crit_show_entry_info(dialog, full_name, already_here, dolly); resp = dialog.get_string(gettext("\nYour decision about file's data:\n[P]reserve\n[O]verwrite\nmark [S]aved and preserve\nmark saved and overwri[T]e\n[R]emove\n[*] keep undefined\n[A]bort\n Your Choice? "), true); if(resp.size() != 1) dialog.message(gettext("Please answer by the character between brackets ('[' and ']') and press return")); else { switch(*resp.begin()) { case 'P': ret = data_preserve; loop = false; break; case 'O': ret = data_overwrite; loop = false; break; case 'S': ret = data_preserve_mark_already_saved; loop = false; break; case 'T': ret = data_overwrite_mark_already_saved; loop = false; break; case 'R': ret = data_remove; loop = false; break; case '*': ret = data_undefined; loop = false; break; case 'A': resp = dialog.get_string(tools_printf(gettext("Warning, are you sure you want to abort (please answer \"%S\" to confirm)? "), &confirm), true); if(resp == confirm) throw Ethread_cancel(false, 0); else dialog.message(gettext("Cancellation no confirmed")); break; default: dialog.message(string(gettext("Unknown choice: ")) + resp); } } } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } over_action_ea op_tools_crit_ask_user_for_EA_action(user_interaction & dialog, const string & full_name, const cat_entree *already_here, const cat_entree *dolly) { over_action_ea ret = EA_undefined; NLS_SWAP_IN; try { const string confirm = gettext("yes"); bool loop = true; string resp; while(loop) { dialog.printf(gettext("Conflict found while selecting the file to retain in the resulting archive:")); dialog.printf(gettext("User Decision requested for EA of file %S"), &full_name); op_tools_crit_show_entry_info(dialog, full_name, already_here, dolly); resp = dialog.get_string(gettext("\nYour decision about file's EA:\n[p]reserve\n[o]verwrite\nmark [s]aved and preserve\nmark saved and overwri[t]e\n[m]erge EA and preserve\nmerge EA a[n]d overwrite\n[r]emove\n[*] keep undefined\n[a]bort\n Your choice? "), true); if(resp.size() != 1) dialog.message(gettext("Please answer by the character between brackets ('[' and ']') and press return")); else { switch(*resp.begin()) { case 'p': ret = EA_preserve; loop = false; break; case 'o': ret = EA_overwrite; loop = false; break; case 's': ret = EA_preserve_mark_already_saved; loop = false; break; case 't': ret = EA_overwrite_mark_already_saved; loop = false; break; case 'm': ret = EA_merge_preserve; loop = false; break; case 'n': ret = EA_merge_overwrite; loop = false; break; case 'r': ret = EA_clear; loop = false; break; case '*': ret = EA_undefined; loop = false; break; case 'a': resp = dialog.get_string(tools_printf(gettext("Warning, are you sure you want to abort (please answer \"%S\" to confirm)? "), &confirm), true); if(resp == confirm) throw Ethread_cancel(false, 0); else dialog.message(gettext("Cancellation no confirmed")); break; default: dialog.message(string(gettext("Unknown choice: ")) + resp); } } } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } over_action_ea op_tools_crit_ask_user_for_FSA_action(user_interaction & dialog, const string & full_name, const cat_entree *already_here, const cat_entree *dolly) { over_action_ea ret = EA_undefined; NLS_SWAP_IN; try { const string confirm = gettext("yes"); bool loop = true; string resp; while(loop) { dialog.printf(gettext("Conflict found while selecting the file to retain in the resulting archive:")); dialog.printf(gettext("User Decision requested for FSA of file %S"), &full_name); op_tools_crit_show_entry_info(dialog, full_name, already_here, dolly); resp = dialog.get_string(gettext("\nYour decision about file's FSA:\n[p]reserve\n[o]verwrite\nmark [s]aved and preserve\nmark saved and overwri[t]e\n[*] keep undefined\n[a]bort\n Your choice? "), true); if(resp.size() != 1) dialog.message(gettext("Please answer by the character between brackets ('[' and ']') and press return")); else { switch(*resp.begin()) { case 'p': ret = EA_preserve; loop = false; break; case 'o': ret = EA_overwrite; loop = false; break; case 's': ret = EA_preserve_mark_already_saved; loop = false; break; case 't': ret = EA_overwrite_mark_already_saved; loop = false; break; case '*': ret = EA_undefined; loop = false; break; case 'a': resp = dialog.get_string(tools_printf(gettext("Warning, are you sure you want to abort (please answer \"%S\" to confirm)? "), &confirm), true); if(resp == confirm) throw Ethread_cancel(false, 0); else dialog.message(gettext("Cancellation no confirmed")); break; default: dialog.message(string(gettext("Unknown choice: ")) + resp); } } } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } void op_tools_crit_show_entry_info(user_interaction & dialog, const string & full_name, const cat_entree *already_here, const cat_entree *dolly) { NLS_SWAP_IN; try { const string yes = gettext("YES"); const string no = gettext("NO"); const cat_inode * al_inode = dynamic_cast(already_here); const cat_directory * al_directory = dynamic_cast(already_here); const cat_file * al_file = dynamic_cast(already_here); const cat_mirage * al_mirage = dynamic_cast(already_here); const cat_inode * do_inode = dynamic_cast(dolly); const cat_directory * do_directory = dynamic_cast(dolly); const cat_file * do_file = dynamic_cast(dolly); const cat_mirage * do_mirage = dynamic_cast(dolly); dialog.printf(gettext("Entry information:\t\"in place\"\t\"to be added\"")); dialog.printf(gettext("Is inode :\t %S \t\t %S"), al_inode == nullptr ? &no : &yes , do_inode == nullptr ? &no : &yes); dialog.printf(gettext("Is directory :\t %S \t\t %S"), al_directory == nullptr ? &no : &yes , do_directory == nullptr ? &no : &yes); dialog.printf(gettext("Is plain file :\t %S \t\t %S"), al_file == nullptr ? &no : &yes , do_file == nullptr ? &no : &yes); dialog.printf(gettext("Is hard linked :\t %S \t\t %S"), al_mirage == nullptr ? &no : &yes , do_mirage == nullptr ? &no : &yes); dialog.printf(gettext("Entry type :\t %s \t %s"), cat_entree_signature2string(already_here->signature()), cat_entree_signature2string(dolly->signature())); if(al_inode != nullptr && do_inode != nullptr) { const string me = gettext("me"); const string notme = ""; bool in_place_data_recent = al_inode->get_last_modif() >= do_inode->get_last_modif(); bool in_place_ea_recent = al_inode->get_last_change() >= do_inode->get_last_change(); bool al_ea_saved = al_inode->ea_get_saved_status() == ea_saved_status::full; bool do_ea_saved = do_inode->ea_get_saved_status() == ea_saved_status::full; bool al_fsa_saved = al_inode->fsa_get_saved_status() == fsa_saved_status::full; bool do_fsa_saved = do_inode->fsa_get_saved_status() == fsa_saved_status::full; dialog.printf(gettext("Data more recent :\t %S \t\t %S"), in_place_data_recent ? &me : ¬me , in_place_data_recent ? ¬me : &me); if(al_file != nullptr && do_file != nullptr) { infinint al_size = al_file->get_size(); infinint do_size = do_file->get_size(); bool al_dirty = al_file->is_dirty(); bool do_dirty = do_file->is_dirty(); bool al_sparse = al_file->get_sparse_file_detection_read(); bool do_sparse = do_file->get_sparse_file_detection_read(); dialog.printf(gettext("Data size :\t %i \t\t %i"), &al_size, &do_size); dialog.printf(gettext("Sparse file :\t %S \t\t %S"), al_sparse ? &yes : &no, do_sparse ? &yes : &no); dialog.printf(gettext("Dirty file :\t %S \t\t %S"), al_dirty ? &yes : &no, do_dirty ? &yes : &no); } dialog.printf(gettext("Data full saved :\t %S \t\t %S"),al_inode->get_saved_status() == saved_status::saved ? &yes:&no , do_inode->get_saved_status() == saved_status::saved ? &yes:&no); dialog.printf(gettext("EA full saved :\t %S \t\t %S"),al_ea_saved ? &yes:&no , do_ea_saved ? &yes:&no); if(al_ea_saved || do_ea_saved) dialog.printf(gettext("EA more recent :\t %S \t\t %S"),in_place_ea_recent ? &me : ¬me , in_place_data_recent ? ¬me : &me); dialog.printf(gettext("FSA full saved :\t %S \t\t %S"), al_fsa_saved ? &yes:&no , do_fsa_saved ? &yes:&no); if(al_fsa_saved || do_fsa_saved) { string al_fam = al_fsa_saved ? fsa_scope_to_string(al_fsa_saved, al_inode->fsa_get_families()) : "-"; string do_fam = do_fsa_saved ? fsa_scope_to_string(do_fsa_saved, do_inode->fsa_get_families()) : "-"; dialog.printf(gettext("FSA familly :\t %S \t\t %S"), &al_fam, &do_fam); } if(al_ea_saved && do_ea_saved) { const ea_attributs *al_ea = al_inode->get_ea(); const ea_attributs *do_ea = do_inode->get_ea(); infinint al_tmp = al_ea->size(); infinint do_tmp = do_ea->size(); dialog.printf(gettext("EA number :\t %i \t\t %i"), &al_tmp, &do_tmp); al_tmp = al_ea->space_used(); do_tmp = do_ea->space_used(); dialog.printf(gettext("EA size :\t %i \t\t %i"), &al_tmp, &do_tmp); } } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } } // end of namespace dar-2.7.15/src/libdar/cat_detruit.hpp0000644000175000017500000000572314636066467014372 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_detruit.hpp /// \brief used to record information in a catalogue about a deleted file (differential backup context) /// \ingroup Private #ifndef CAT_DETRUIT_HPP #define CAT_DETRUIT_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_nomme.hpp" #include "datetime.hpp" #include "archive_version.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the deleted file entry class cat_detruit : public cat_nomme { public : cat_detruit(const std::string & name, unsigned char firm, const datetime & date) : cat_nomme(name, saved_status::saved) , del_date(date) { signe = firm; }; cat_detruit(const smart_pointer & pdesc, const archive_version & reading_ver, bool small); cat_detruit(const cat_nomme & ref): cat_nomme(ref), del_date(0) { signe = ref.signature(); }; cat_detruit(const cat_detruit & ref) = default; cat_detruit(cat_detruit && ref) noexcept = default; cat_detruit & operator = (const cat_detruit & ref) = default; cat_detruit & operator = (cat_detruit && ref) = default; ~cat_detruit() = default; virtual bool operator == (const cat_entree & ref) const override; unsigned char get_signature() const { return signe; }; void set_signature(unsigned char x) { signe = x; }; const datetime & get_date() const { return del_date; }; void set_date(const datetime & ref) { del_date = ref; }; /// inherited from cat_entree virtual unsigned char signature() const override { return 'x'; }; /// inherited from cat_entree virtual std::string get_description() const override { return "deleted file"; }; /// inherited from cat_entree virtual cat_entree *clone() const override { return new (std::nothrow) cat_detruit(*this); }; protected: virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; private : unsigned char signe; datetime del_date; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/erreurs_ext.cpp0000644000175000017500000000401414636066467014415 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "erreurs_ext.hpp" using namespace std; namespace libdar { Ethread_cancel_with_attr::Ethread_cancel_with_attr(bool now, U_64 x_flag, const infinint & attr) : Ethread_cancel(now, x_flag) { x_attr = new (nothrow) infinint(attr); if(x_attr == nullptr) throw Ememory("Ethread_cancel_with_attr::Ethread_cancel_with_attr"); } Ethread_cancel_with_attr & Ethread_cancel_with_attr::operator = (Ethread_cancel_with_attr && ref) noexcept { Ethread_cancel::operator = (move(ref)); swap(x_attr, ref.x_attr); return *this; } void Ethread_cancel_with_attr::detruit() { try { if(x_attr != nullptr) { delete x_attr; x_attr = nullptr; } } catch(...) { // do nothing } } void Ethread_cancel_with_attr::copy_from(const Ethread_cancel_with_attr & ref) { x_attr = new (nothrow) infinint(*ref.x_attr); if(x_attr == nullptr) throw Ememory("Ethread_cancel_with_attr::Ethread_cancel_with_attr"); } } // end of namespace dar-2.7.15/src/libdar/cat_device.hpp0000644000175000017500000000573214636066467014151 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_device.hpp /// \brief parent class for all special devices inodes /// \ingroup Private #ifndef CAT_DEVICE_HPP #define CAT_DEVICE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_inode.hpp" #include "integers.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the special cat_device root class class cat_device : public cat_inode { public : cat_device(const infinint & uid, const infinint & gid, U_16 perm, const datetime & last_access, const datetime & last_modif, const datetime &last_change, const std::string & name, U_16 major, U_16 minor, const infinint & fs_device); cat_device(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small); cat_device(const cat_device & ref) = default; cat_device(cat_device && ref) noexcept = default; cat_device & operator = (const cat_device & ref) = default; cat_device & operator = (cat_device && ref) = default; ~cat_device() = default; virtual bool operator == (const cat_entree & ref) const override; int get_major() const { if(get_saved_status() != saved_status::saved) throw SRC_BUG; else return xmajor; }; int get_minor() const { if(get_saved_status() != saved_status::saved) throw SRC_BUG; else return xminor; }; void set_major(int x) { xmajor = x; }; void set_minor(int x) { xminor = x; }; // using method is_more_recent_than() from cat_inode class // using method has_changed_since() from cat_inode class // signature is left pure abstract protected : virtual void sub_compare(const cat_inode & other, bool isolated_mode) const override; virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; private : U_16 xmajor, xminor; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/slice_layout.cpp0000644000175000017500000000651114636066467014546 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "slice_layout.hpp" using namespace std; namespace libdar { void slice_layout::read(generic_file & f) { char tmp; first_size.read(f); other_size.read(f); first_slice_header.read(f); other_slice_header.read(f); if(f.read(&tmp , 1) == 1) { switch(tmp) { case OLDER_THAN_V8: older_sar_than_v8 = true; break; case V8: older_sar_than_v8 = false; break; default: throw SRC_BUG; } } else throw Erange("slice_layout::read", gettext("Missing data while reading slice_layout object")); } void slice_layout::write(generic_file & f) const { char tmp = older_sar_than_v8 ? OLDER_THAN_V8 : V8; first_size.dump(f); other_size.dump(f); first_slice_header.dump(f); other_slice_header.dump(f); f.write(&tmp, 1); } void slice_layout::clear() { first_size = 0; other_size = 0; first_slice_header = 0; other_slice_header = 0; older_sar_than_v8 = false; } void slice_layout::which_slice(const infinint & offset, infinint & slice_num, infinint & slice_offset) const { // considering particular case of a non-sliced archive if(first_size.is_zero() || other_size.is_zero()) { slice_num = 1; if(offset < first_slice_header) slice_offset = first_slice_header; else slice_offset = offset - first_slice_header; return; } // sanity checks if(first_size < first_slice_header) throw SRC_BUG; if(other_size < other_slice_header) throw SRC_BUG; if(first_slice_header.is_zero()) throw SRC_BUG; if(other_slice_header.is_zero()) throw SRC_BUG; // end of sanity checks infinint byte_in_first_file = first_size - first_slice_header; infinint byte_per_file = other_size - other_slice_header; if(!older_sar_than_v8) { --byte_in_first_file; --byte_per_file; // this is due to the trailing flag (one byte length) } if(offset < byte_in_first_file) { slice_num = 1; slice_offset = offset + first_slice_header; } else { euclide(offset - byte_in_first_file, byte_per_file, slice_num, slice_offset); slice_num += 2; // "+2" because file number starts to 1 and first file is already counted slice_offset += other_slice_header; } } } // end of namespace dar-2.7.15/src/libdar/candidates.cpp0000644000175000017500000000661114636066467014152 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "candidates.hpp" using namespace std; namespace libdar { void candidates::add(archive_num val, db_etat st) { // considering the new entry to add switch(st) { case db_etat::et_saved: clear(); break; case db_etat::et_removed: case db_etat::et_absent: if(ewr) return; // stop here complete ignoring the new entry clear(); break; case db_etat::et_patch: case db_etat::et_present: case db_etat::et_inode: break; case db_etat::et_patch_unusable: clear(); break; default: throw SRC_BUG; } // considering the current stack status if(status.size() > 0) { if(st == db_etat::et_present) return; // not useful to add et_present when something is already in switch(status.back()) { case db_etat::et_saved: case db_etat::et_patch: break; case db_etat::et_present: if(status.size() > 1) // et_present only allowed in status if it is the first entry throw SRC_BUG; else clear(); break; case db_etat::et_inode: num.pop_back(); // removing the top of the stack status.pop_back(); // removing the top of the stack break; case db_etat::et_removed: case db_etat::et_absent: clear(); break; case db_etat::et_patch_unusable: return; // stop here default: throw SRC_BUG; } } // now adding the new entry num.push_back(val); status.push_back(st); } db_lookup candidates::get_status() const { // considering the bottom of the stack if(status.size() > 0) { switch(status.front()) { case db_etat::et_saved: return db_lookup::found_present; case db_etat::et_patch: case db_etat::et_inode: case db_etat::et_present: case db_etat::et_patch_unusable: return db_lookup::not_restorable; case db_etat::et_removed: case db_etat::et_absent: return db_lookup::found_removed; default: throw SRC_BUG; } } else return db_lookup::not_found; } void candidates::set_the_set(set & archive) const { deque::const_iterator it = num.begin(); deque::const_iterator et = status.begin(); archive.clear(); while(it != num.end() && et != status.end()) { archive.insert(*it); ++it; ++et; } if(it != num.end() || et != status.end()) throw SRC_BUG; } } // end of namespace dar-2.7.15/src/libdar/fichier_libcurl.hpp0000644000175000017500000002231514640016434015164 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file fichier_libcurl.hpp /// \brief class fichier_libcurl definition. This is a full implementation/inherited class of class fichier_global /// this type of object are generated by entrepot_libcurl. /// \ingroup Private #ifndef FICHIER_LIBCURL_HPP #define FICHIER_LIBCURL_HPP #include "../my_config.h" extern "C" { #if LIBCURL_AVAILABLE #if HAVE_CURL_CURL_H #include #endif #endif } // end extern "C" #include #ifdef LIBTHREADAR_AVAILABLE #include #endif #include "integers.hpp" #include "user_interaction.hpp" #include "fichier_global.hpp" #include "mycurl_protocol.hpp" #include "mycurl_easyhandle_node.hpp" namespace libdar { /// \addtogroup Private /// @{ #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) /// libcurl remote files class fichier_libcurl : public fichier_global, protected libthreadar::thread { public: /// constructor fichier_libcurl(const std::shared_ptr & dialog, //< for user interaction requested by fichier_global const std::string & chemin, //< full path of the file to open mycurl_protocol proto, //< to workaround some libcurl strange behavior for some protocols const std::shared_ptr & handle, //< the easy handle wrapper object gf_mode m, //< open mode U_I waiting, //< retry timeout in case of network error bool force_permission, //< whether file permission should be modified U_I permission, //< file permission to enforce if force_permission is set bool erase); //< whether to erase the file before writing to it /// no copy constructor available /// \note because we inherit from libthreadar::thread we has no copy constructor fichier_libcurl(const fichier_libcurl & ref) = delete; /// no move constructor fichier_libcurl(fichier_libcurl && ref) = delete; /// no assignment operator ///\note because we inherit from libthreadar::thread that has not copy constructor fichier_libcurl & operator = (const fichier_libcurl & ref) = delete; /// no move operator fichier_libcurl & operator = (fichier_libcurl && ref) = delete; /// destructor ~fichier_libcurl() noexcept { kill(); join(); detruit(); }; /// change the permission of the file virtual void change_permission(U_I perm) override; /// set the ownership of the file virtual void change_ownership(const std::string & user, const std::string & group) override { throw Efeature(gettext("user/group ownership not supported for this repository")); }; // not supported /// return the size of the file virtual infinint get_size() const override; /// set posix_fadvise for the whole file virtual void fadvise(advise adv) const override {}; // not supported and ignored // inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return pos == get_position(); }; virtual infinint get_position() const override { return current_offset; }; protected: // inherited from generic_file grand-parent class virtual void inherited_read_ahead(const infinint & amount) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override; virtual void inherited_flush_read() override; virtual void inherited_terminate() override; // inherited from fichier_global parent class virtual U_I fichier_global_inherited_write(const char *a, U_I size) override; virtual bool fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) override; // inherited from thread virtual void inherited_run() override; private: static const U_I tampon_size = CURL_MAX_WRITE_SIZE; ////////////////////////////// // // implementation internals // ////////////////////////////// // the object has two modes: // - meta data mode (skip, get_position() and other non read/write operations) // - data mode (read or write operations) // // in metadata mode each method is a simple code execution (no subthread, no callback) // // in data mode, a subthread is used to interact with libcurl. It sends or receives // data through the interthread pipe. A callback is occasionally run by libcurl in this // subthread. // in read mode, the subthread is run only if the interthread is empty. the subthread may // survive the inherited_read call and may suspend on writing data to interthread being full // - "subthread_net_offset" is updated by the callback and read by the subthread when // libcurl has returned it keeps trace of the amount of data sent to interthread. // - "network_block" is set by the main thread to define the amount of data to be fetched. It // it used to setup libcurl and is read by the subthread for control/validation purposes bool end_data_mode; ///< true if subthread has been requested to end bool sub_is_dying; ///< is set by subthread when about to end bool sync_write_asked; ///< ask subthread to use 'synchronize' tell all data has been written bool weof; ///< whether write EOF has been signaled to libcurl std::shared_ptr ehandle; ///< easy handle (wrapped in C++ object) that we modify when necessary bool metadatamode; ///< wether we are acting on metadata rather than file's data infinint current_offset; ///< current offset we are reading / writing at bool has_maxpos; ///< true if maxpos is set infinint maxpos; ///< in read mode this is the filesize, in write mode this the offset where to append data (not ovewriting) bool append_write; ///< whether we should append to data (and not replace) when uploading U_I meta_inbuf; ///< amount of byte available in "meta_tampon" U_I wait_delay; ///< time in second to wait before retrying in case of network error infinint network_block; ///< maximum amount of data read at once from the network (only read by subthread) infinint subthread_net_offset; ///< updated by sub thread in network block mode to give amount of bytes pushed to interthread infinint subthread_cur_offset; ///< subthread copy of current_offset libthreadar::fast_tampon interthread; ///< data channel for reading or writing with subthread libthreadar::barrier synchronize; ///< used to be sure subthread has been launched // also used for sync_write mycurl_protocol x_proto; ///< used to workaround some libcurl strange behavoir for some protocols void set_range(const infinint & begin, const infinint & range_size); ///< set range in easyhandle void unset_range(); ///< unset range in easyhandle void switch_to_metadata(bool mode);///< set to true to get or set file's metadata, false to read/write file's data void detruit(); ///< get ready for object destruction void run_thread(); ///< run subthread with the previously defined parameters void stop_thread(); ///< ask subthread to stop and wait for its end void relaunch_thread(const infinint & block_size); ///< re-run the subthread if not running void initialize_subthread(); ///< subthread routine to init itself void finalize_subthread(); ///< subthread routine to end itself void set_subthread(U_I & needed_bytes); ///< set parameters and run subthtread if necessary bool still_data_to_write(); /// return true when in write mode and there is data pending to writing in interthread static size_t write_data_callback(char *buffer, size_t size, size_t nmemb, void *userp); static size_t read_data_callback(char *bufptr, size_t size, size_t nitems, void *userp); static size_t write_meta_callback(char *buffer, size_t size, size_t nmemb, void *userp); static size_t read_meta_callback(char *bufptr, size_t size, size_t nitems, void *userp); }; #endif /// @} } // end of namespace #endif dar-2.7.15/src/libdar/scrambler.hpp0000644000175000017500000000655314636066467014037 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file scrambler.hpp /// \brief contains the definition of the scrambler class, a very weak encryption scheme /// \ingroup Private #ifndef SCRAMBLER_HPP #define SCRAMBLER_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "generic_file.hpp" #include "erreurs.hpp" #include "secu_string.hpp" namespace libdar { /// \addtogroup Private /// @{ /// \brief scrambler is a very weak encryption scheme class scrambler : public generic_file { public: scrambler(const secu_string & pass, generic_file & hidden_side); scrambler(const scrambler & ref) = delete; scrambler(scrambler && ref) noexcept = delete; scrambler & operator = (const scrambler & ref) = delete; scrambler & operator = (scrambler && ref) noexcept = delete; ~scrambler() { if(buffer != nullptr) delete [] buffer; }; virtual bool skippable(skippability direction, const infinint & amount) override { if(ref == nullptr) throw SRC_BUG; return ref->skippable(direction, amount); }; virtual bool skip(const infinint & pos) override { if(ref == nullptr) throw SRC_BUG; return ref->skip(pos); }; virtual bool skip_to_eof() override { if(ref == nullptr) throw SRC_BUG; return ref->skip_to_eof(); }; virtual bool skip_relative(S_I x) override { if(ref == nullptr) throw SRC_BUG; return ref->skip_relative(x); }; virtual bool truncatable(const infinint & pos) const override { if(ref == nullptr) throw SRC_BUG; return ref->truncatable(pos); }; virtual infinint get_position() const override { if(ref == nullptr) throw SRC_BUG; return ref->get_position(); }; protected: virtual void inherited_read_ahead(const infinint & amount) override { if(ref == nullptr) throw SRC_BUG; ref->read_ahead(amount); }; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override { if(ref == nullptr) throw SRC_BUG; ref->truncate(pos); }; virtual void inherited_sync_write() override {}; // nothing to do virtual void inherited_flush_read() override {}; // nothing to do virtual void inherited_terminate() override {}; // nothing to do private: secu_string key; U_I len; generic_file *ref; unsigned char *buffer; U_I buf_size; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/contextual.hpp0000644000175000017500000000650214636066467014245 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file contextual.hpp /// \brief class contextual adds the information of phases in the generic_file /// \ingroup Private #ifndef CONTEXTUAL_HPP #define CONTEXTUAL_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "erreurs.hpp" #include "label.hpp" #include namespace libdar { #define CONTEXT_INIT "init" #define CONTEXT_OP "operation" #define CONTEXT_LAST_SLICE "last_slice" /// \addtogroup Private /// @{ /// the contextual class adds the information of phases in the generic_file /// several phases are defined like for example /// - INIT phase /// - OPERATIONAL phase /// - LAST SLICE phase /// . /// these are used to help the command launched between slices to /// decide the action to do depending on the context when reading an archive /// (first slice / last slice read, ...) /// the context must also be transfered to dar_slave through the pair of tuyau objects /// /// this class also support some additional informations common to all 'level1' layer of /// archive, such as: /// - the data_name information /// class contextual { public : contextual() { status = ""; }; contextual(const contextual & ref) = default; contextual(contextual && ref) noexcept = default; contextual & operator = (const contextual & ref) = default; contextual & operator = (contextual && ref) noexcept = default; virtual ~contextual() noexcept(false) {}; /// defines the new contextual value /// \note inherited class may redefine this call but /// but must call the parent method to set the value /// contextual:set_info_status() virtual void set_info_status(const std::string & s) { status = s; }; /// get the current contextual value virtual std::string get_info_status() const { return status; }; /// returns whether the archive is a old archive (format < 8) virtual bool is_an_old_start_end_archive() const = 0; /// obtain the data_name of the archive (label associated with the archive's data) /// \note label are conserved with dar_xform and archive isolation, but are /// not with archive merging or archive creation (full or differential backup) virtual const label & get_data_name() const = 0; private: std::string status; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_tube.cpp0000644000175000017500000000255614636066467013645 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_tube.hpp" using namespace std; namespace libdar { bool cat_tube::operator == (const cat_entree & ref) const { const cat_tube *ref_tube = dynamic_cast(&ref); if(ref_tube == nullptr) return false; else return cat_inode::operator == (ref); } } // end of namespace dar-2.7.15/src/libdar/parallel_tronconneuse.hpp0000644000175000017500000006014414636067146016452 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file parallel_tronconneuse.hpp /// \brief defines a block structured file. /// \ingroup Private /// Several classes are defined here: /// - class parallel_tronconneuse, which has similar interface and behavior as class tronconneuse /// - class read_below, is used internally by parallel_tronconneuse in read-only mode to workers from data of the underlay /// - class write_below, is used internally by parallel_tronconneuse in write-only mode to write down the data produced by the workers /// - class crypto_workers, that transform data between parallel_tronconneuse and the read_below class or the write_below class /// depending on the context. Several objects of this class executes in parallel the crypto_module routine, but there is /// only one read_below or write_below thread, the class parallel_tronconneuse stays executed by the main/original/calling thread /// . #ifndef PARALLEL_TRONCONNEUSE_HPP #define PARALLEL_TRONCONNEUSE_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "archive_version.hpp" #include "crypto_segment.hpp" #include "heap.hpp" #include "crypto_module.hpp" #include "proto_tronco.hpp" #include namespace libdar { /// \addtogroup Private /// @{ // those class are used by the parallel_tronconneuse class to wrap the different // type of threads. They are defined just after the parallel_tronconneuse definition class read_below; class write_below; class crypto_worker; /// status flags used between parallel_tronconneuse and its sub-threads enum class tronco_flags { normal = 0, stop = 1, eof = 2, die = 3, data_error = 4, exception_below = 5, exception_worker = 6, exception_error = 7 }; ///////////////////////////////////////////////////// // // the parallel_tronconneuse class that orchestrate all that // // /// this is a partial implementation of the generic_file interface to cypher/decypher data block by block. /// This class is a pure virtual one, as several calls have to be defined by inherited classes /// - encrypted_block_size_for /// - clear_block_allocated_size_for /// - encrypt_data /// - decrypt_data /// . /// parallel_tronconneuse is either read_only or write_only, read_write is not allowed. /// The openning mode is defined by encrypted_side's mode. /// In write_only no skip() is allowed, writing is sequential from the beginning of the file to the end /// (like writing to a pipe). /// In read_only all skip() functions are available. class parallel_tronconneuse : public proto_tronco { public: /// This is the constructor /// \param[in] workers is the number of worker threads /// \param[in] block_size is the size of block encryption (the size of clear data encrypted toghether). /// \param[in] encrypted_side where encrypted data are read from or written to. /// \param[in] reading_ver version of the archive format /// \param[in] ptr pointer to an crypto_module object that will be passed to the parallel_tronconneuse /// \note that encrypted_side is not owned and destroyed by tronconneuse, it must exist during all the life of the /// tronconneuse object, and is not destroyed by the tronconneuse's destructor parallel_tronconneuse(U_I workers, U_32 block_size, generic_file & encrypted_side, const archive_version & reading_ver, std::unique_ptr & ptr); /// copy constructor parallel_tronconneuse(const parallel_tronconneuse & ref) = delete; /// move constructor parallel_tronconneuse(parallel_tronconneuse && ref) = default; /// assignment operator parallel_tronconneuse & operator = (const parallel_tronconneuse & ref) = delete; /// move operator parallel_tronconneuse & operator = (parallel_tronconneuse && ref) noexcept = default; /// destructor ~parallel_tronconneuse() noexcept; /// inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override; /// inherited from generic_file virtual bool skip(const infinint & pos) override; /// inherited from generic_file virtual bool skip_to_eof() override; /// inherited from generic_file virtual bool skip_relative(S_I x) override; /// inherited from generic_file virtual bool truncatable(const infinint & pos) const override { return false; }; /// inherited from generic_file virtual infinint get_position() const override { if(is_terminated()) throw SRC_BUG; return current_position; }; /// in write_only mode indicate that end of file is reached /// this call must be called in write mode to purge the /// internal cache before deleting the object (else some data may be lost) /// no further write call is allowed /// \note this call cannot be used from the destructor, because it relies on pure virtual methods virtual void write_end_of_file() override { if(is_terminated()) throw SRC_BUG; sync_write(); }; /// this method to modify the initial shift. This overrides the constructor "no_initial_shift" of the constructor virtual void set_initial_shift(const infinint & x) override; /// let the caller give a callback function that given a generic_file with cyphered data, is able /// to return the offset of the first clear byte located *after* all the cyphered data, this /// callback function is used (if defined by the following method), when reaching End of File. virtual void set_callback_trailing_clear_data(trailing_clear_data_callback call_back) override; /// returns the block size given to constructor virtual U_32 get_clear_block_size() const override { return clear_block_size; }; private: // inherited from generic_file /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_read_ahead(const infinint & amount) override; /// this protected inherited method is now private for inherited classes of tronconneuse virtual U_I inherited_read(char *a, U_I size) override; /// inherited from generic_file /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_write(const char *a, U_I size) override; /// this prorected inherited method is now private for inherited classed of tronconneuse /// \note no skippability in write mode, so no truncate possibility neither, this call /// will always throw a bug exception virtual void inherited_truncate(const infinint & pos) override { throw SRC_BUG; }; /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_sync_write() override; /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_flush_read() override; /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_terminate() override; const archive_version & get_reading_version() const { return reading_ver; }; // internal data structure enum class thread_status { running, suspended, dead }; // the fields U_I num_workers; ///< number of worker threads U_32 clear_block_size; ///< size of a clear block infinint current_position; ///< current position for the upper layer perspective (modified by skip*, inherited_read/write, find_offset_in_lus_data) infinint initial_shift; ///< the offset in the "encrypted" below layer at which starts the encrypted data archive_version reading_ver; ///< archive format we follow std::unique_ptr crypto; ///< the crypto module use to cipher / uncipher block of data infinint (*mycallback)(generic_file & below, const archive_version & reading_ver); generic_file* encrypted; // fields used to represent possible status of subthreads and communication channel (the pipe) U_I ignore_stop_acks; ///< how much stop ack still to be read (aborted stop order context) thread_status t_status; ///< wehther child thread are waiting us on the barrier // the following stores data from the ratelier_gather to be provided for read() operation // the lus_data/lus_flags is what is extracted from the ratelier_gather, both constitute // the feedback channel from sub-threads to provide order acks and normal data std::deque > lus_data; std::deque lus_flags; bool lus_eof; bool check_bytes_to_skip; ///< whether to check for bytes to skip // the following stores data going to ratelier_scatter for the write() operation std::unique_ptr tempo_write; infinint block_num; // the datastructures shared among threads std::shared_ptr > scatter; std::shared_ptr > gather; std::shared_ptr waiter; std::shared_ptr > tas; // the child threads std::deque > travailleur; std::unique_ptr crypto_reader; std::unique_ptr crypto_writer; /// send and order to subthreads and gather acks from them /// \param[in] order is the order to send to the subthreads /// \param[in] for_offset is not zero is the offset we want to skip to /// (it is only taken into account when order is stop) /// \note with order stop and non zero for_offset, if the /// data at for_offset is found while purging the /// ratelier_gather, the purge is stopped, false is returned and /// the ignore_stop field is set to true, meaning that a stop order /// has not been purged from the pipe and should be ignored when its /// acknolegements will be met. In any other case true is returned, /// the subthreaded got the order and the ratelier has been purged. bool send_read_order(tronco_flags order, const infinint & for_offset = 0); /// send order in write mode void send_write_order(tronco_flags order); /// wake up threads in read mode when necessary void go_read(); /// fill lus_data/lus_flags from ratelier_gather if these are empty void read_refill(); /// purge the ratelier from the next order which is provided as returned value /// \param[in] pos if pos is not zero, the normal data located at /// pos offset is also looked for. If it is found before any order /// the call returns tronco_flags::normal and the order is not purged /// but the ignore_stop_acks fields is set to true for further /// reading to skip over this order acknolegments. tronco_flags purge_ratelier_from_next_order(infinint pos = 0); /// removing the ignore_stop_acks pending on the pipe /// \param[in] pos if pos is not zero and normal data is found at /// pos offset before all stop acks could get read, the call stops /// and return false. Else true is returned meaning all stop acks /// has been read and removed from the pope /// \note this method acts the same pas purge_ratelier_from_next_order but fetch /// from ratelier up to data offset met of the pending ack to be all read /// but it then stops, it does not look for a real order after /// the pending stop acks of an aborted stop order bool purge_unack_stop_order(const infinint & pos = 0); /// flush lus_data/lus_flags up to requested pos offset to be found or all data has been removed /// \param[in] pos the data offset we look for /// \return true if the offset has been found and is /// ready for next inherited_read() call, false else /// and lus_data/lus_flags have been empties of /// the first non-order blocks found (data blocks) /// \note current_position is set upon success else /// it is unchanged but will not match what may still /// remain in the pipe bool find_offset_in_lus_data(const infinint & pos); /// reset the interthread datastructure and launch the threads void run_threads(); /// end threads taking into account the fact they may be suspended on the barrier void stop_threads(); /// call by join_threads() below just code simplification around exception handling void join_workers_only(); /// wait for threads to finish and eventually rethrow their exceptions in current thread void join_threads(); static U_I get_ratelier_size(U_I num_worker) { return num_worker + num_worker/2; }; static U_I get_heap_size(U_I num_worker); }; ///////////////////////////////////////////////////// // // read_below subthread used by parallel_tronconneuse // to dispatch chunk of encrypted data to the workers // class read_below: public libthreadar::thread { public: read_below(const std::shared_ptr > & to_workers, ///< where to send chunk of crypted data const std::shared_ptr & waiter, ///< barrier used for synchronization with workers and the thread that called us U_I num_workers, ///< how much workers have to be informed when special condition occurs U_I clear_block_size, ///< clear block size used generic_file* encrypted_side, ///< the encrypted file we fetch data from and slice in chunks for the workers const std::shared_ptr > xtas, ///< heap of pre-allocated memory for the chunks infinint init_shift): ///< the offset at which the encrypted data is expected to start workers(to_workers), waiting(waiter), num_w(num_workers), clear_buf_size(clear_block_size), encrypted(encrypted_side), tas(xtas), initial_shift(init_shift), reof(false), trailing_clear_data(nullptr) { flag = tronco_flags::normal; }; ~read_below() { if(ptr) tas->put(move(ptr)); kill(); join(); }; /// let the caller give a callback function that given a generic_file with mixed cyphered and clear data, is able /// to return the offset of the first clear byte located *after* all the cyphered data, this /// callback function is used (if defined by the following method), when reaching End of File. void set_callback_trailing_clear_data(trailing_clear_data_callback call_back) { trailing_clear_data = call_back; }; // *** // // *** the method above should not be used anymore once the thread is running *** // // *** // /// set the initial shift must be called right after set_flag(stop) to take effect void set_initial_shift(const infinint & x) { initial_shift = x; }; /// available for the parent thread to skip at another positon (read mode) /// \param[in] pos is the offset (from the upper layer / clear data point /// of view) at which we should seek /// \note changes asked by this call do not take effect until set_flag(stop) /// is invoked void set_pos(const infinint & pos) { skip_to = pos; }; /// method for the parent thread to send a control order /// \note the parent thread must cleanup the gather object, it should /// expect to receive N block with the flag given to set_flag (N being /// the number of workers. Then this parent thread by a call the the /// waiter.Wait() barrier method will /// release all the worker and this below thread that will feed the /// pipe with new data taken at the requested offset provided by /// mean of the above set_pos() method. /// \note example: ask to stop before skipping void set_flag(tronco_flags val) { flag = val; }; /// method available for the parent thread to know the clear offset of the new flow /// \note after set_pos()+set_flag() the new flow may start /// a bit before the position requested by set_pos() due to /// block of encryption boundaries. So the caller should ignore /// some first bytes of data received const infinint & get_clear_flow_start() const { return clear_flow_start; }; /// method available for the parent thread to know the amount of bytes to skip in the new flow /// \note this is the difference between the argument provided /// to set_pos() and the value returned by get_clear_flow_start() /// in other words this is the amount of bytes to skip in the /// new flow, in order to const infinint & get_pos_in_flow() const { return pos_in_flow; }; protected: virtual void inherited_run() override; private: std::shared_ptr > workers; ///< object used to scatter data to workers std::shared_ptr waiting; ///< barrier used to synchronize with worker and parent thread U_I num_w; ///< number of worker thread we are feeding through the ratelier_scatter U_I clear_buf_size; ///< amount of clear data per encrypted chunk generic_file* encrypted; ///< the encrypted data archive_version version; ///< archive version format std::shared_ptr > tas; ///< where to fetch from blocks of data infinint initial_shift; ///< initial shift bool reof; ///< whether we reached eof while reading trailing_clear_data_callback trailing_clear_data; ///< callback function that gives the amount of clear data found at the end of the given file std::unique_ptr ptr; ///< current segment we are setting up infinint index_num; ///< current crypto block index // initialized by inherited_run() / get_ready_for_new_offset() infinint crypt_offset; ///< position to skip to in 'below' to start reading crypted data U_I encrypted_buf_size; ///< size of the encrypted chunk of data // fields accessible by both the caller and the read_below thread infinint skip_to; ///< modification to this field is only done by the parent thread at a time it is not read by this thread tronco_flags flag; ///< modification to this type is atomic so we do not use mutex infinint clear_flow_start; ///< modification of this field is only done by this thread a at time the parent thread does not read it infinint pos_in_flow; ///< modification of this field is only done by this thread a at time the parent thread does not read it void work(); infinint get_ready_for_new_offset(); void send_flag_to_workers(tronco_flags theflag); // same function as the tronconneuse::position_clear2crypt void position_clear2crypt(const infinint & pos, infinint & file_buf_start, infinint & clear_buf_start, infinint & pos_in_buf, infinint & block_num); }; ///////////////////////////////////////////////////// // // write_below subthread used by parallel_tronconneuse // to gather and write down encrypted data work from workers // class write_below: public libthreadar::thread { public: write_below(const std::shared_ptr > & from_workers, const std::shared_ptr & waiter, U_I num_workers, generic_file* encrypted_side, const std::shared_ptr > xtas): workers(from_workers), waiting(waiter), num_w(num_workers), cur_num_w(0), encrypted(encrypted_side), tas(xtas), error(false), error_block(0) { if(encrypted == nullptr) throw SRC_BUG; }; ~write_below() { kill(); join(); }; bool exception_pending() const { return error; }; const infinint & get_error_block() const { return error_block; }; protected: virtual void inherited_run() override; private: std::shared_ptr > workers; std::shared_ptr waiting; U_I num_w; U_I cur_num_w; generic_file* encrypted; ///< the encrypted data std::shared_ptr > tas; bool error; infinint error_block; // last crypto block before error std::deque >ones; std::deque flags; void work(); }; ///////////////////////////////////////////////////// // // the crypto_worker threads performing ciphering/deciphering // of many data blocks in parallel // class crypto_worker: public libthreadar::thread { public: crypto_worker(std::shared_ptr > & read_side, std::shared_ptr > & write_side, std::shared_ptr waiter, std::unique_ptr && ptr, bool encrypt): reader(read_side), writer(write_side), waiting(waiter), crypto(move(ptr)), do_encrypt(encrypt), abort(status::fine) { if(!reader || !writer || !waiting || !crypto) throw SRC_BUG; }; virtual ~crypto_worker() { kill(); join(); }; protected: virtual void inherited_run() override; private: enum class status { fine, inform, sent }; std::shared_ptr > & reader; std::shared_ptr > & writer; std::shared_ptr waiting; std::unique_ptr crypto; bool do_encrypt; // if false do decrypt std::unique_ptr ptr; unsigned int slot; status abort; void work(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/wrapperlib.hpp0000644000175000017500000001621314636066467014226 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file wrapperlib.hpp /// \brief libz and libbz2 wrapper to have identical interface to these libraries. /// \ingroup Private /// /// libz and libbz2 library differ in the way they return values /// in certain circumpstances. This module defines the wrapperlib class /// that make their use homogeneous. #ifndef WRAPPERLIB_HPP #define WRAPPERLIB_HPP #include "../my_config.h" extern "C" { #if HAVE_ZLIB_H && LIBZ_AVAILABLE #include #endif #if HAVE_BZLIB_H && LIBBZ2_AVAILABLE #include #endif #if HAVE_LZMA_H && LIBLZMA_AVAILABLE #include #endif } // end extern "C" #include "integers.hpp" namespace libdar { /// \addtogroup Private /// @{ const int WR_OK = 0; // operation successful const int WR_MEM_ERROR = 1; // lack of memory const int WR_VERSION_ERROR = 2; // incompatible version of the compression library with the one expected by libdar const int WR_STREAM_ERROR = 3; // not a valid compression level, incoherent data provided to the compression library const int WR_DATA_ERROR = 4; // data has been corrupted const int WR_NO_FLUSH = 5; // parameter to let the compression library decide at which time to output data (from zlib Z_NO_FLUSH, no other way is done in libdar) const int WR_BUF_ERROR = 6; // no possible work to perform for the request action without additional provided data/or storage space to the compression library const int WR_STREAM_END = 7; // end of compressed data met const int WR_FINISH = 8; // parameter requiring the compression library to cleanly stop the running operation enum wrapperlib_mode { zlib_mode, bzlib_mode, xz_mode }; /// this class encapsulates calls to libz or libbz2 /// this is mainly an adaptation of libbz2 specificities to /// have libb2 acting exactly as libz does. class wrapperlib { public: wrapperlib(wrapperlib_mode mode); wrapperlib(const wrapperlib & ref) = delete; wrapperlib(wrapperlib && ref) noexcept = delete; wrapperlib & operator = (const wrapperlib & ref) = delete; wrapperlib & operator = (wrapperlib && ref) noexcept = delete; ~wrapperlib(); void set_next_in(const char *x) { return (this->*x_set_next_in)(x); }; void set_avail_in(U_I x) { return (this->*x_set_avail_in)(x); }; U_I get_avail_in() const { return (this->*x_get_avail_in)(); }; U_64 get_total_in() const { return (this->*x_get_total_in)(); }; void set_next_out(char *x) { return (this->*x_set_next_out)(x); }; char *get_next_out() const { return (this->*x_get_next_out)(); }; void set_avail_out(U_I x) { return (this->*x_set_avail_out)(x); }; U_I get_avail_out() const { return (this->*x_get_avail_out)(); }; U_64 get_total_out() const { return (this->*x_get_total_out)(); }; S_I compressInit(U_I compression_level) { level = compression_level; return (this->*x_compressInit)(compression_level); }; S_I decompressInit() { return (this->*x_decompressInit)(); }; S_I compressEnd() { return (this->*x_compressEnd)(); }; S_I decompressEnd() { return (this->*x_decompressEnd)(); }; S_I compress(S_I flag) { return (this->*x_compress)(flag); }; S_I decompress(S_I flag) { return (this->*x_decompress)(flag);}; S_I compressReset(); S_I decompressReset(); private: #if LIBZ_AVAILABLE z_stream *z_ptr; #endif #if LIBBZ2_AVAILABLE bz_stream *bz_ptr; #endif #if LIBLZMA_AVAILABLE lzma_stream *lzma_ptr; #endif S_I level; void (wrapperlib::*x_set_next_in)(const char *x); void (wrapperlib::*x_set_avail_in)(U_I x); U_I (wrapperlib::*x_get_avail_in)() const; U_64 (wrapperlib::*x_get_total_in)() const; void (wrapperlib::*x_set_next_out)(char *x); char *(wrapperlib::*x_get_next_out)() const; void (wrapperlib::*x_set_avail_out)(U_I x); U_I (wrapperlib::*x_get_avail_out)() const; U_64 (wrapperlib::*x_get_total_out)() const; S_I (wrapperlib::*x_compressInit)(U_I compression_level); S_I (wrapperlib::*x_decompressInit)(); S_I (wrapperlib::*x_compressEnd)(); S_I (wrapperlib::*x_decompressEnd)(); S_I (wrapperlib::*x_compress)(S_I flag); S_I (wrapperlib::*x_decompress)(S_I flag); // set of routines for zlib #if LIBZ_AVAILABLE S_I z_compressInit(U_I compression_level); S_I z_decompressInit(); S_I z_compressEnd(); S_I z_decompressEnd(); S_I z_compress(S_I flag); S_I z_decompress(S_I flag); void z_set_next_in(const char *x); void z_set_avail_in(U_I x); U_I z_get_avail_in() const; U_64 z_get_total_in() const; void z_set_next_out(char *x); char *z_get_next_out() const; void z_set_avail_out(U_I x); U_I z_get_avail_out() const; U_64 z_get_total_out() const; #endif // set of routines for bzlib #if LIBBZ2_AVAILABLE S_I bz_compressInit(U_I compression_level); S_I bz_decompressInit(); S_I bz_compressEnd(); S_I bz_decompressEnd(); S_I bz_compress(S_I flag); S_I bz_decompress(S_I flag); void bz_set_next_in(const char *x); void bz_set_avail_in(U_I x); U_I bz_get_avail_in() const; U_64 bz_get_total_in() const; void bz_set_next_out(char *x); char *bz_get_next_out() const; void bz_set_avail_out(U_I x); U_I bz_get_avail_out() const; U_64 bz_get_total_out() const; #endif // set of routines for liblzma #if LIBLZMA_AVAILABLE S_I lzma_compressInit(U_I compression_level); S_I lzma_decompressInit(); S_I lzma_end(); S_I lzma_encode(S_I flag); void lzma_set_next_in(const char *x); void lzma_set_avail_in(U_I x); U_I lzma_get_avail_in() const; U_64 lzma_get_total_in() const; void lzma_set_next_out(char *x); char *lzma_get_next_out() const; void lzma_set_avail_out(U_I x); U_I lzma_get_avail_out() const; U_64 lzma_get_total_out() const; #endif }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mycurl_protocol.hpp0000644000175000017500000000362014636066467015311 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mycurl_protocol.hpp /// \brief datastructure defining the network protocols available for entrepot_libcurl class /// \ingroup API #ifndef MYCURL_PROTOCOL_HPP #define MYCURL_PROTOCOL_HPP #include "../my_config.h" extern "C" { } // end extern "C" namespace libdar { /// \addtogroup API /// @{ /// libcurl protocols supported by libdar /// \note dar needs to list a directory content to find /// the last slice available in a directory, which /// feature is not always available with http and https protocols /// and never available with scp protocol /// Thus dar only supports ftp and sftp enum mycurl_protocol { proto_ftp, ///< FTP file transfer protocol proto_sftp ///< SFTP Secure FTP (over ssh) }; /// extract mycurl_protocol from a given URL extern mycurl_protocol string_to_mycurl_protocol(const std::string & arg); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/macro_tools.hpp0000644000175000017500000004427514636067146014404 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file macro_tools.hpp /// \brief macroscopic tools for libdar internals /// \ingroup Private #ifndef MACRO_TOOLS_HPP #define MACRO_TOOLS_HPP #include "../my_config.h" extern "C" { #if HAVE_LIMITS_H #include #endif } #include #include #include "catalogue.hpp" #include "compression.hpp" #include "infinint.hpp" #include "header_version.hpp" #include "generic_file.hpp" #include "crypto.hpp" #include "pile.hpp" #include "entrepot.hpp" #include "range.hpp" #include "slice_layout.hpp" #include "tuyau.hpp" #include "trivial_sar.hpp" #include "proto_compressor.hpp" #define BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFFER_SIZE #undef BUFFER_SIZE #define BUFFER_SIZE SSIZE_MAX #endif #endif namespace libdar { /// \addtogroup Private /// @{ constexpr U_I GLOBAL_ELASTIC_BUFFER_SIZE = 51200; extern const archive_version macro_tools_supported_version; extern const std::string LIBDAR_STACK_LABEL_UNCOMPRESSED; extern const std::string LIBDAR_STACK_LABEL_CLEAR; extern const std::string LIBDAR_STACK_LABEL_UNCYPHERED; extern const std::string LIBDAR_STACK_LABEL_LEVEL1; /// create an container to write an archive to a pipe extern trivial_sar *macro_tools_open_archive_tuyau(const std::shared_ptr & dialog, S_I fd, gf_mode mode, const label & internal_name, const label & data_name, bool slice_header_format_07, const std::string & execute); /// setup the given pile object to contain a stack of generic_files suitable to read an archive /// \note the stack has the following contents depending on given options /// /// +-top LIBDAR_STACK_LABEL_ /// +-------------------------------------------------------+---------------------+ /// | compressor |v v v | /// +-------------------------------------------------------+---------------------+ /// | [ escape ] |v v v | /// +-------------------------------------------------------+v---v---v------------+ /// | [ cache ] | (parallel_)tronconneuse | scrambler |v v v | /// | [ tronc ] |LEVEL 1 | /// | trivial_sar | zapette | sar |v v v | /// +-------------------------------------------------------+---------------------+ /// +-bottom /// /// \note escape is present unless tape mark have been disabled at archive creation time /// \note tronc is not present in sequential read mode /// \note cache layer is present only in absence of encryption and when no escape layer is above /// \note _UNCOMPRESSED label is associated to the top of the stack /// \note _CLEAR is associated to the generic_thread below compressor else escape else /// the cache or crypto_sym or scrambler which then has two Labels (_CLEAR and _UNCYPHERED) extern void macro_tools_open_archive(const std::shared_ptr & dialog, ///< for user interaction const std::shared_ptr & where, ///< slices location const std::string &basename, ///< slice basename const infinint & min_digits, ///< minimum digits for the slice number const std::string &extension, ///< slice extensions crypto_algo crypto, ///< encryption algorithm const secu_string &pass, ///< pass key for crypto/scrambling U_32 crypto_size, ///< crypto block size pile & stack, ///< the stack of generic_file resulting of the archive openning header_version &ver, ///< header read from raw data const std::string &input_pipe, ///< named pipe for input when basename is "-" (dar_slave) const std::string &output_pipe, ///< named pipe for output when basename is "-" (dar_slave) const std::string & execute, ///< command to execute between slices infinint & second_terminateur_offset, ///< where to start looking for the second terminateur (set to zero if there is only one terminateur). bool lax, ///< whether we skip&warn the usual verifications bool has_external_cat, ///< true if the catalogue will not be read from the current archive (flag used in lax mode only) bool sequential_read, ///< whether to use the escape sequence (if present) to get archive contents and proceed to sequential reading bool info_details, ///< be or not verbose about the archive openning std::list & gnupg_signed, ///< list of existing signature found for that archive (valid or not) slice_layout & sl, ///< slicing layout of the archive (read from sar header if present) U_I multi_threaded_crypto, ///< number of worker thread to run for cryptography (1 -> tronconneuse object, more -> parallel_tronconneuse object) U_I multi_threaded_compress, ///< number of worker threads to compress/decompress (need compression_block_size > 0) bool header_only ///< if true, stop the process before openning the encryption layer ); // all allocated objects (ret1, ret2, scram), must be deleted when no more needed by the caller of this routine /// uses terminator to skip to the position where to find the catalogue and read it, taking care of having this catalogue pointing to the real data (context of isolated catalogue --- cata_stack --- used to rescue an internal archive --- data_stack) extern catalogue *macro_tools_get_derivated_catalogue_from(const std::shared_ptr & dialog, pile & data_stack, // where to get the files and EA from pile & cata_stack, // where to get the catalogue from const header_version & ver, // version format as defined in the header of the archive to read bool info_details, // verbose display (throught user_interaction) infinint &cat_size, // return size of archive in file (not in memory !) const infinint & second_terminateur_offset, // location of the second terminateur (zero if none exist) std::list & signatories, // returns the list of signatories (empty if archive is was not signed) bool lax_mode); // whether to do relaxed checkings /// uses terminator to skip to the position where to find the catalogue and read it extern catalogue *macro_tools_get_catalogue_from(const std::shared_ptr & dialog, pile & stack, // raw data access object const header_version & ver, // version format as defined in the header of the archive to read bool info_details, // verbose display (throught user_interaction) infinint &cat_size, // return size of archive in file (not in memory !) const infinint & second_terminateur_offset, std::list & signatories, // returns the list of signatories (empty if archive is was not signed) bool lax_mode); /// read the catalogue from cata_stack assuming the cata_stack is positionned at the beginning of the area containing archive's dumped data extern catalogue *macro_tools_read_catalogue(const std::shared_ptr & dialog, const header_version & ver, const pile_descriptor & cata_pdesc, const infinint & cat_size, std::list & signatories, bool lax_mode, const label & lax_layer1_data_name, bool only_detruits); extern catalogue *macro_tools_lax_search_catalogue(const std::shared_ptr & dialog, pile & stack, const archive_version & edition, compression compr_algo, bool info_details, bool even_partial_catalogues, const label & layer1_data_name); // return the offset of the beginning of the catalogue. extern infinint macro_tools_get_terminator_start(generic_file & f, const archive_version & reading_ver); /// build layers for a new archive /// \param[in] dialog for user interaction /// \param[out] layers the resulting stack of generic_file layers ready for use /// \param[out] ver the archive "header/trailer" to be dropped at beginning and end of archive /// \param[out] slicing slicing layout of the created archive (resulting from sar layers if present according to the provided first/file_size provided below) /// \param[in] ref_slicing if not nullptr the pointed to slicing_layout will be stored in the header/trailer version of the archive /// \param[in] sauv_path_t where to create the archive /// \param[in] filename archive base name /// \param[in] extension archive extension /// \param[in] allow_over whether to allow slice overwriting /// \param[in] warn_over whether to warn before overwriting /// \param[in] info_details whether to display detailed information /// \param[in] pause how many slices to wait before pausing (0 to never wait) /// \param[in] algo compression algorithm /// \param[in] compression_level compression level /// \param[in] compression_block_size if set to zero use streaming compression else use block compression of the given size /// \param[in] file_size size of the slices /// \param[in] first_file_size size of the first slice /// \param[in] execute command to execute after each slice creation /// \param[in] crypto cipher algorithm to use /// \param[in] pass password/passphrase to use for encryption /// \param[in] crypto_size size of crypto blocks /// \param[in] gnupg_recipients list of email recipients'public keys to encrypt a randomly chosen key with /// \param[in] gnupg_signatories list of email which associated signature has to be used to sign the archive /// \param[in] empty dry-run execution (null_file at bottom of the stack) /// \param[in] slice_permission permission to set the slices to /// \param[in] add_marks_for_sequential_reading whether to add an escape layer in the stack /// \param[in] user_comment user comment to add into the slice header/trailer /// \param[in] hash algorithm to use for slices hashing /// \param[in] slice_min_digits minimum number of digits slice number must have /// \param[in] internal_name common label to all slices /// \param[in] data_name to use in slice header /// \param[in] iteration_count used for key derivation when passphrase is human provided /// \param[in] kdf_hash hash algorithm used for the key derivation function /// \param[in] multi_threaded_crypto number of worker threads to handle cryptography stuff /// \param[in] multi_threaded_compress number of worker threads to handle compression (block mode only) /// /// \note the stack has the following contents depending on given options /// /// +-top LIBDAR_STACK_LABEL_ /// +---------------------------------------------------------+---------------------+ /// | compressor | | /// +---------------------------------------------------------+---------------------+ /// | [ escape ] | | /// +-- - - - - - - - - - - - - - - - - - - - - - - - - - - - +---------------------+ /// | cache | (paralle_)tronconneuse | scrambler | | /// | [ cache ] |_CACHE_PIPE | /// | trivial_sar | null_file | sar | | /// +---------------------------------------------------------+---------------------+ /// +-bottom /// /// \note the bottom cache layer is only present when trivial_sar is used to write on a pipe. /// trivial_sar used to write a non sliced archive does not use a cache layer above it /// \note the top cache is only used in place of crypto_sym or scrambler when no encryption /// is required and the cache layer labelled _CACHE_PIPE is absent. /// \note escape layer is present by default unless tape marks have been disabled /// \note the generic_thread are inserted in the stack if multi-threading is possible and allowed /// extern void macro_tools_create_layers(const std::shared_ptr & dialog, pile & layers, header_version & ver, slice_layout & slicing, const slice_layout *ref_slicing, const std::shared_ptr & sauv_path_t, const std::string & filename, const std::string & extension, bool allow_over, bool warn_over, bool info_details, const infinint & pause, compression algo, U_I compression_level, U_I compression_block_size, const infinint & file_size, const infinint & first_file_size, const std::string & execute, crypto_algo crypto, const secu_string & pass, U_32 crypto_size, const std::vector & gnupg_recipients, const std::vector & gnupg_signatories, bool empty, const std::string & slice_permission, bool add_marks_for_sequential_reading, const std::string & user_comment, hash_algo hash, const infinint & slice_min_digits, const label & internal_name, const label & data_name, const infinint & iteration_count, hash_algo kdf_hash, U_I multi_threaded_crypto, U_I multi_threaded_compress); /// dumps the catalogue and close all the archive layers to terminate the archive /// \param[in] dialog for user interaction /// \param[in] layers the archive layers to close /// \param[in] ver the archive "header" to be dropped at end of archive /// \param[in] cat the catalogue to dump in the layer before closing the archive /// \param[in] info_details whether to display detailed information /// \param[in] crypto cipher algorithm used in "layers" /// \param[in] gnupg_recipients used sign the catalog, use an empty vector if there is no signatories (no nedd to sign the hash of the catalogue) /// \param[in] gnupg_signatories used to sign the catalog, use an empty vector to disable signning /// \param[in] algo compression algorithm used /// \param[in] empty dry-run execution (null_file at bottom of the stack) extern void macro_tools_close_layers(const std::shared_ptr & dialog, pile & layers, const header_version & ver, const catalogue & cat, bool info_details, crypto_algo crypto, compression algo, const std::vector & gnupg_recipients, const std::vector & gnupg_signatories, bool empty); /// gives the location of data EA and FSA (when they are saved) of the object given in argument /// \param[in] obj a pointer to the object which data & EFSA is to be located /// \param[in] sl slice layout of the archive /// \return a set of slices which will be required to restore that particular file (over the slice(s) /// containing the catalogue of course). extern range macro_tools_get_slices(const cat_nomme *obj, slice_layout sl); /// open a pair of tuyau objects encapsulating two named pipes. /// \param[in,out] dialog for user interaction /// \param[in] input path to the input named pipe /// \param[in] output path to the output named pipe /// \param[out] in resulting tuyau object for input /// \param[out] out resulting tuyau object for output /// \note in and out parameters must be released by the caller thanks to the "delete" operator extern void macro_tools_open_pipes(const std::shared_ptr & dialog, const std::string &input, const std::string & output, tuyau *&in, tuyau *&out); /// return a proto_compressor object realizing the desired (de)compression level/aglo on top of "base" in streaming mode /// \param[in] algo the compression algorithm to use /// \param[in,out] base the layer to read from or write to compressed data /// \param[in] compression_level the compression level to use (when compressing data) /// \param[in] num_workers for the few algorithm that allow multi-thread compression (lz4 actually) extern proto_compressor* macro_tools_build_streaming_compressor(compression algo, generic_file & base, U_I compression_level, U_I num_workers); /// return a proto_compressor object realizing the desired (de)compression level/algo on to of "base" in block mode /// \param[in] algo the compression algorithm to use /// \param[in,out] base the layer to read from or write to compressed data /// \param[in] compression_level the compression level to use (when compressing data) /// \param[in] num_workers for the few algorithm that allow multi-thread compression (lz4 actually) /// \param[in] block_size size of the data block extern proto_compressor* macro_tools_build_block_compressor(compression algo, generic_file & base, U_I compression_level, U_I num_workers, U_I block_size); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/user_interaction_blind.hpp0000644000175000017500000000511414636066467016602 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file user_interaction_blind.hpp /// \brief defines the interaction between libdar and a non communcant "blind" user /// \note yes blind people do communicate, no offense to them, this class just /// "blindly" answer and do never show messages to a particular user /// \ingroup API #ifndef USER_INTERACTION_BLIND_HPP #define USER_INTERACTION_BLIND_HPP #include "../my_config.h" #include #include "user_interaction.hpp" #include "secu_string.hpp" namespace libdar { /// \addtogroup API /// @{ /// full implementation class for user_interaction, which shows nothing and assumes answer "no" to any question class user_interaction_blind : public user_interaction { public: user_interaction_blind() {}; user_interaction_blind(const user_interaction_blind & ref) = default; user_interaction_blind(user_interaction_blind && ref) noexcept = default; user_interaction_blind & operator = (const user_interaction_blind & ref) = default; user_interaction_blind & operator = (user_interaction_blind && ref) noexcept = default; ~user_interaction_blind() = default; protected: virtual void inherited_message(const std::string & message) override { }; // do nothing virtual bool inherited_pause(const std::string & message) override { return false; }; virtual std::string inherited_get_string(const std::string & message, bool echo) override { return "user_interaction_blind, is blindly answering no"; }; virtual secu_string inherited_get_secu_string(const std::string & message, bool echo) override { return secu_string(); }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive5.cpp0000644000175000017500000000564014636067146013555 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // extern "C" #include "archive5.hpp" #include "erreurs.hpp" #include "shell_interaction_emulator.hpp" #define ARCHIVE_NOT_EXPLOITABLE "Archive of reference given is not exploitable" using namespace std; using libdar::Erange; using libdar::Ebug; namespace libdar5 { void archive::op_listing(user_interaction & dialog, const archive_options_listing & options) { libdar::shell_interaction_emulator emul(&dialog); emul.archive_show_contents(*this, options); } bool archive::get_children_of(user_interaction & dialog, const std::string & dir) { if(!dialog.get_use_listing()) throw Erange("archive::get_childen_of", gettext("listing() method must be given")); return libdar::archive::get_children_of(listing_callback, &dialog, dir); } void archive::listing_callback(const string & the_path, const libdar::list_entry & entry, void *context) { user_interaction *dialog = (user_interaction *)(context); const std::string & flag = entry.get_data_flag() + entry.get_delta_flag() + entry.get_ea_flag() + entry.get_fsa_flag() + entry.get_compression_ratio_flag() + entry.get_sparse_flag(); const std::string & perm = entry.get_perm(); const std::string & uid = entry.get_uid(true); const std::string & gid = entry.get_gid(true); const std::string & size = entry.get_file_size(true); //<<<<< listing sizes in bytes const std::string & date = entry.get_last_modif(); const std::string & filename = entry.get_name(); bool is_dir = entry.is_dir(); bool has_children = !entry.is_empty_dir(); if(dialog == nullptr) throw SRC_BUG; if(dialog->get_use_listing()) dialog->listing(flag, perm, uid, gid, size, date, filename, is_dir, has_children); else throw SRC_BUG; } } // end of namespace dar-2.7.15/src/libdar/filesystem_diff.cpp0000644000175000017500000001425014636066467015225 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif #if HAVE_TIME_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_SYS_UN_H #include #endif #if HAVE_UNISTD_H #include #endif #if STDC_HEADERS #include #endif #ifdef LIBDAR_NODUMP_FEATURE #if HAVE_SYS_IOCTL_H #include #endif #if LIBDAR_NODUMP_FEATURE == NODUMP_LINUX #include #else #if LIBDAR_NODUMP_FEATURE == NODUMP_EXT2FS #include #else #error "unknown location of ext2_fs.h include file" #endif #endif #endif #if MAJOR_IN_MKDEV #include #if !defined(makedev) && defined(mkdev) #define makedev(a,b) mkdev((a),(b)) #endif #else #if MAJOR_IN_SYSMACROS #include #endif #endif } // end extern "C" #include #include #include "filesystem_diff.hpp" #include "tools.hpp" #include "filesystem_tools.hpp" #include "erreurs.hpp" #include "user_interaction.hpp" #include "cat_all_entrees.hpp" #include "ea_filesystem.hpp" #include "cygwin_adapt.hpp" #include "fichier_local.hpp" #include "generic_rsync.hpp" #include "null_file.hpp" using namespace std; namespace libdar { filesystem_diff::filesystem_diff(const std::shared_ptr & dialog, const path &root, bool x_info_details, const mask & x_ea_mask, bool x_alter_atime, bool x_furtive_read_mode, const fsa_scope & scope): filesystem_hard_link_read(dialog, x_furtive_read_mode, scope) { fs_root = nullptr; ea_mask = nullptr; current_dir = nullptr; try { fs_root = filesystem_tools_get_root_with_symlink(*dialog, root, x_info_details); if(fs_root == nullptr) throw Ememory("filesystem_diff::filesystem_diff"); info_details = x_info_details; ea_mask = x_ea_mask.clone(); if(ea_mask == nullptr) throw Ememory("filesystem_diff::filesystem_diff"); alter_atime = x_alter_atime; furtive_read_mode = x_furtive_read_mode; current_dir = nullptr; reset_read(); } catch(...) { detruire(); throw; } zeroing_negative_dates_without_asking(); // when reading existing inode from filesystem } void filesystem_diff::reset_read() { corres_reset(); if(current_dir != nullptr) delete current_dir; current_dir = new (nothrow) path(*fs_root); filename_pile.clear(); if(current_dir == nullptr) throw Ememory("filesystem_diff::reset_read"); const string display = current_dir->display(); const char* tmp = display.c_str(); cat_entree *ref = make_read_entree(*current_dir, "", true, *ea_mask); cat_directory *ref_dir = dynamic_cast(ref); try { if(ref_dir != nullptr) { filename_struct rfst; rfst.last_acc = ref_dir->get_last_access(); rfst.last_mod = ref_dir->get_last_modif(); filename_pile.push_back(rfst); } else if(ref == nullptr) throw Erange("filesystem_diff::reset_read", string(gettext("Non existent file: ")) + tmp); else throw Erange("filesystem_diff::reset_read", string(gettext("File must be a directory: ")) + tmp); } catch(...) { if(ref != nullptr) delete ref; throw; } if(ref != nullptr) delete ref; } bool filesystem_diff::read_filename(const string & name, cat_nomme * &ref) { cat_directory *ref_dir = nullptr; if(current_dir == nullptr) throw SRC_BUG; ref = make_read_entree(*current_dir, name, false, *ea_mask); if(ref == nullptr) return false; // no file of that name else { ref_dir = dynamic_cast(ref); if(ref_dir != nullptr) { filename_struct rfst; rfst.last_acc = ref_dir->get_last_access(); rfst.last_mod = ref_dir->get_last_modif(); filename_pile.push_back(rfst); *current_dir += ref_dir->get_name(); } return true; } } void filesystem_diff::skip_read_filename_in_parent_dir() { if(filename_pile.empty()) throw SRC_BUG; string tmp; if(!alter_atime && !furtive_read_mode) tools_noexcept_make_date(current_dir->display(), false, filename_pile.back().last_acc, filename_pile.back().last_mod, filename_pile.back().last_mod); filename_pile.pop_back(); current_dir->pop(tmp); } void filesystem_diff::detruire() { if(fs_root != nullptr) { delete fs_root; fs_root = nullptr; } if(current_dir != nullptr) { delete current_dir; current_dir = nullptr; } if(ea_mask != nullptr) { delete ea_mask; ea_mask = nullptr; } } } // end of namespace dar-2.7.15/src/libdar/compile_time_features.cpp0000644000175000017500000001336714636067146016420 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "integers.hpp" #include "infinint.hpp" #include "tools.hpp" #include "compile_time_features.hpp" #if HAVE_LIBTHREADAR_LIBTHREADAR_HPP #include #endif extern "C" { #if LIBCURL_AVAILABLE #if HAVE_CURL_CURL_H #include #endif #endif } namespace libdar { namespace compile_time { bool ea() noexcept { #ifdef EA_SUPPORT return true; #else return false; #endif } bool largefile() noexcept { #if defined( _FILE_OFFSET_BITS ) || defined( _LARGE_FILES ) return true; #else return sizeof(off_t) > 4; #endif } bool nodump() noexcept { return FSA_linux_extX(); } bool special_alloc() noexcept { return false; } U_I bits() noexcept { #ifdef LIBDAR_MODE return LIBDAR_MODE; #else return 0; // infinint #endif } bool thread_safe() noexcept { #if defined( MUTEX_WORKS ) && !defined ( MISSING_REENTRANT_LIBCALL ) return true; #else return false; #endif } bool libz() noexcept { #if LIBZ_AVAILABLE return true; #else return false; #endif } bool libbz2() noexcept { #if LIBBZ2_AVAILABLE return true; #else return false; #endif } bool liblzo() noexcept { #if LIBLZO2_AVAILABLE return true; #else return false; #endif } bool libxz() noexcept { #if LIBLZMA_AVAILABLE return true; #else return false; #endif } bool libzstd() noexcept { #if LIBZSTD_AVAILABLE return true; #else return false; #endif } bool liblz4() noexcept { #if LIBLZ4_AVAILABLE return true; #else return false; #endif } bool libgcrypt() noexcept { #if CRYPTO_AVAILABLE return true; #else return false; #endif } bool libargon2() noexcept { #if LIBARGON2_AVAILABLE return true; #else return false; #endif } bool furtive_read() noexcept { #if FURTIVE_READ_MODE_AVAILABLE return true; #else return false; #endif } endian system_endian() noexcept { endian ret; try { ret = infinint::is_system_big_endian() ? big : little; } catch(...) { ret = error; } return ret; } bool posix_fadvise() noexcept { #if HAVE_POSIX_FADVISE return true; #else return false; #endif } bool fast_dir() noexcept { #if LIBDAR_FAST_DIR return true; #else return false; #endif } bool FSA_linux_extX() noexcept { #ifdef LIBDAR_NODUMP_FEATURE return true; #else return false; #endif } bool FSA_birthtime() noexcept { #ifdef LIBDAR_BIRTHTIME return true; #else return false; #endif } bool Linux_statx() noexcept { #ifdef HAVE_STATX_SYSCALL return true; #else return false; #endif } bool microsecond_read() noexcept { #if LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND || LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND return true; #else return false; #endif } bool nanosecond_read() noexcept { #if LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND return true; #else return false; #endif } bool microsecond_write() noexcept { #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND || LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND return true; #else return false; #endif } bool nanosecond_write() noexcept { #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND return true; #else return false; #endif } bool symlink_restore_dates() noexcept { #ifdef HAVE_LUTIMES return true; #else return false; #endif } bool public_key_cipher() noexcept { #ifdef GPGME_SUPPORT return true; #else return false; #endif } bool libthreadar() noexcept { #ifdef LIBTHREADAR_AVAILABLE return true; #else return false; #endif } std::string libthreadar_version() noexcept { std::string ret = ""; #ifdef LIBTHREADAR_AVAILABLE unsigned int min, med, maj; libthreadar::get_version(maj, med, min); #ifdef LIBTHREADAR_BARRIER_MAC std::string barrier_flavor = libthreadar::barrier::used_implementation(); std::string barrier_impl = tools_printf(gettext("barrier using %S"), &barrier_flavor); #else std::string barrier_impl = ""; #endif ret = tools_printf("%d.%d.%d - %S", maj, med, min, &barrier_impl); #endif return ret; } bool librsync() noexcept { #if LIBRSYNC_AVAILABLE return true; #else return false; #endif } bool remote_repository() noexcept { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) return true; #else return false; #endif } std::string libcurl_version() noexcept { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) return curl_version(); #else return ""; #endif } } // end of compile_time nested namespace } // end of libdar namespace dar-2.7.15/src/libdar/cat_door.cpp0000644000175000017500000000375214636066467013650 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_door.hpp" #include "null_file.hpp" using namespace std; namespace libdar { bool cat_door::operator == (const cat_entree & ref) const { const cat_door *ref_door = dynamic_cast(&ref); if(ref_door == nullptr) return false; else return cat_file::operator == (ref); } generic_file *cat_door::get_data(get_data_mode mode, shared_ptr delta_sig, U_I signature_block_size, shared_ptr delta_ref, const crc**checksum) const { generic_file *ret = nullptr; // we never calculate delta signature for door inode if(delta_sig) delta_sig->reset(); if(status == from_path) { ret = new (nothrow) null_file(gf_read_only); if(ret == nullptr) throw Ememory("cat_door::get_data"); } else ret = cat_file::get_data(mode, nullptr, signature_block_size, nullptr, checksum); return ret; } } // end of namespace dar-2.7.15/src/libdar/database5.cpp0000644000175000017500000001655614636067146013710 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if STDC_HEADERS #include #endif #if HAVE_ERRNO_H #include #endif } // end extern "C" #include #include #include #include "database5.hpp" #include "user_interaction.hpp" #include "deci.hpp" #include "tools.hpp" #include "storage.hpp" #include "database_header.hpp" #include "path.hpp" #include "generic_file.hpp" #include "data_tree.hpp" #include "erreurs.hpp" #include "nls_swap.hpp" using namespace libdar5; using namespace std; // from data_tree.hpp using libdar::data_tree; // from erreurs.hpp using libdar::Egeneric; using libdar::Ebug; namespace libdar5 { void database::show_contents(user_interaction & dialog) const { NLS_SWAP_IN; try { libdar::database_archives_list content = get_contents(); string opt = libdar::tools_concat_vector(" ", get_options()); if(!dialog.get_use_dar_manager_contents()) { string compr = compression2string(get_compression()); string dar_path = get_dar_path(); string db_version = get_database_version(); dialog.warning(""); dialog.printf(gettext("dar path : %S"), &dar_path); dialog.printf(gettext("dar options : %S"), &opt); dialog.printf(gettext("database version: %S"), &db_version); dialog.printf(gettext("compression used: %S"), &compr); dialog.warning(""); dialog.printf(gettext("archive # | path | basename")); dialog.printf("------------+--------------+---------------"); } string road, base; for(archive_num i = 1; i < content.size(); ++i) { road = content[i].get_path(); base = content[i].get_basename(); if(dialog.get_use_dar_manager_contents()) dialog.dar_manager_contents(i, road, base); else { opt = (road == "") ? gettext("") : road; dialog.printf(" \t%u\t%S\t%S", i, &opt, &base); } } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::show_files(user_interaction & dialog, archive_num num, const database_used_options & opt) const { NLS_SWAP_IN; try { get_files(show_files_callback, &dialog, num, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::show_version(user_interaction & dialog, path chemin) const { NLS_SWAP_IN; try { get_version(get_version_callback, &dialog, chemin); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::show_most_recent_stats(user_interaction & dialog) const { NLS_SWAP_IN; try { if(!dialog.get_use_dar_manager_statistics()) { dialog.printf(gettext(" archive # | most recent/total data | most recent/total EA")); dialog.printf(gettext("--------------+-------------------------+-----------------------")); // having it with gettext let the translater adjust columns width } libdar::database::show_most_recent_stats(statistics_callback, &dialog); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::show_files_callback(void *tag, const std::string & filename, bool available_data, bool available_ea) { user_interaction *dialog = (user_interaction *)(tag); if(dialog == nullptr) throw SRC_BUG; if(dialog->get_use_dar_manager_show_files()) dialog->dar_manager_show_files(filename, available_data, available_ea); else { string etat = ""; if(available_data) etat += gettext("[ Saved ]"); else etat += gettext("[ ]"); if(available_ea) etat += gettext("[ EA ]"); else etat += gettext("[ ]"); dialog->printf("%S %S", &etat, &filename); } } void database::get_version_callback(void *tag, archive_num num, db_etat data_presence, bool has_data_date, datetime data, db_etat ea_presence, bool has_ea_date, datetime ea) { const string REMOVED = gettext("removed "); const string PRESENT = gettext("present "); const string SAVED = gettext("saved "); const string ABSENT = gettext("absent "); const string PATCH = gettext("patch "); const string BROKEN = gettext("BROKEN "); const string INODE = gettext("inode "); const string NO_DATE = " "; string data_state; string ea_state; string data_date; string ea_date; user_interaction *dialog = (user_interaction *)(tag); if(dialog == nullptr) throw SRC_BUG; switch(data_presence) { case db_etat::et_saved: data_state = SAVED; break; case db_etat::et_patch: data_state = PATCH; break; case db_etat::et_patch_unusable: data_state = BROKEN; break; case db_etat::et_inode: data_state = INODE; break; case db_etat::et_present: data_state = PRESENT; break; case db_etat::et_removed: data_state = REMOVED; break; case db_etat::et_absent: data_state = ABSENT; break; default: throw SRC_BUG; } switch(ea_presence) { case db_etat::et_saved: ea_state = SAVED; break; case db_etat::et_present: ea_state = PRESENT; break; case db_etat::et_removed: ea_state = REMOVED; break; case db_etat::et_absent: throw SRC_BUG; // state not used for EA case db_etat::et_patch: throw SRC_BUG; case db_etat::et_patch_unusable: throw SRC_BUG; default: throw SRC_BUG; } if(!has_data_date) { data_state = ABSENT; data_date = NO_DATE; } else data_date = tools_display_date(data); if(!has_ea_date) { ea_state = ABSENT; ea_date = NO_DATE; } else ea_date = tools_display_date(ea); if(dialog->get_use_dar_manager_show_version()) dialog->dar_manager_show_version(num, data_date, data_state, ea_date, ea_state); else dialog->printf(" \t%u\t%S %S %S %S", num, &data_date, &data_state, &ea_date, &ea_state); } void database::statistics_callback(void *tag, U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea) { user_interaction *dialog = (user_interaction *)(tag); if(dialog == nullptr) throw SRC_BUG; if(dialog->get_use_dar_manager_statistics()) dialog->dar_manager_statistics(number, data_count, total_data, ea_count, total_ea); else dialog->printf("\t%u %i/%i \t\t\t %i/%i", number, &data_count, &total_data, &ea_count, &total_ea); } } // end of namespace dar-2.7.15/src/libdar/lz4_module.cpp0000644000175000017500000001117714636066467014134 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif } #include "lz4_module.hpp" #include "tools.hpp" #include using namespace std; namespace libdar { lz4_module::lz4_module(U_I compression_level) { #if LIBLZ4_AVAILABLE if(compression_level > 9 || compression_level < 1) throw Erange("lz4_module::lz4_module", tools_printf(gettext("out of range LZ4 compression level: %d"), compression_level)); acceleration = 10 - compression_level; state = new (nothrow) char[LZ4_sizeofState()]; if(state == nullptr) throw Ememory("lz4_module::lz4_module"); #else throw Ecompilation(gettext("lz4 compression")); #endif } lz4_module::lz4_module(const lz4_module & ref) { #if LIBLZ4_AVAILABLE state = new(nothrow) char[LZ4_sizeofState()]; if(state == nullptr) throw Ememory("lz4_module::lz4_module"); // no need to copy the content of state acceleration = ref.acceleration; #else throw Ecompilation(gettext("lz4 compression")); #endif } lz4_module::lz4_module(lz4_module && ref) noexcept { state = nullptr; swap(state, ref.state); acceleration = move(ref.acceleration); } lz4_module & lz4_module::operator = (const lz4_module & ref) { acceleration = ref.acceleration; // the content of the *state* field does not // matter, it is only used during // the LZ4_compress_fast_extState() // and its content is not used as input // for this function. return *this; } lz4_module & lz4_module::operator = (lz4_module && ref) noexcept { acceleration = move(ref.acceleration); swap(state, ref.state); return *this; } lz4_module::~lz4_module() noexcept { if(state != nullptr) delete [] state; } U_I lz4_module::get_max_compressing_size() const { #if LIBLZ4_AVAILABLE return LZ4_MAX_INPUT_SIZE; #else throw Ecompilation(gettext("lz4 compression")); #endif } U_I lz4_module::get_min_size_to_compress(U_I clear_size) const { #if LIBLZ4_AVAILABLE if(clear_size > get_max_compressing_size() || clear_size < 1) throw Erange("lz4_module::get_min_size_to_compress", "out of range block size submitted to lz4_module::get_min_size_to_compress"); return LZ4_compressBound(clear_size); #else throw Ecompilation(gettext("lz4 compression")); #endif } U_I lz4_module::compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const { #if LIBLZ4_AVAILABLE S_I ret; if(normal_size > get_max_compressing_size()) throw Erange("lz4_module::compress_data", "oversized uncompressed data given to LZ4 compression engine"); ret = LZ4_compress_fast_extState((void *)state, normal, zip_buf, normal_size, zip_buf_size, acceleration); if(ret <= 0) throw Erange("lz4_module::compress_data", "undersized compressed buffer given to LZ4 compression engine"); return ret; #else throw Ecompilation(gettext("lz4 compression")); #endif } U_I lz4_module::uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const { #if LIBLZ4_AVAILABLE S_I ret = LZ4_decompress_safe(zip_buf, normal, zip_buf_size, normal_size); if(ret < 0) throw Edata(gettext("corrupted compressed data met")); return ret; #else throw Ecompilation(gettext("lz4 compression")); #endif } unique_ptr lz4_module::clone() const { #if LIBLZ4_AVAILABLE try { return std::make_unique(*this); } catch(bad_alloc &) { throw Ememory("lz4_module::clone"); } #else throw Ecompilation(gettext("lz4 compression")); #endif } } // end of namespace dar-2.7.15/src/libdar/wrapperlib.cpp0000644000175000017500000003736714636066467014236 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "wrapperlib.hpp" #include "erreurs.hpp" #define CHECK_Z if(z_ptr == nullptr) throw SRC_BUG #define CHECK_BZ if(bz_ptr == nullptr) throw SRC_BUG #define CHECK_LZMA if(lzma_ptr == nullptr) throw SRC_BUG; using namespace std; namespace libdar { #if LIBZ_AVAILABLE static S_I zlib2wrap_code(S_I code); static S_I wrap2zlib_code(S_I code); #endif #if LIBBZ2_AVAILABLE static S_I bzlib2wrap_code(S_I code); static S_I wrap2bzlib_code(S_I code); #endif #if LIBLZMA_AVAILABLE static S_I lzma2wrap_code(S_I code); static lzma_action wrap2lzma_code(S_I code); #endif wrapperlib::wrapperlib(wrapperlib_mode mode) { switch(mode) { case zlib_mode: #if LIBZ_AVAILABLE z_ptr = new (nothrow) z_stream; if(z_ptr == nullptr) throw Ememory("wrapperlib::wrapperlib"); #if LIBBZ2_AVAILABLE bz_ptr = nullptr; #endif #if LIBLZMA_AVAILABLE lzma_ptr = nullptr; #endif z_ptr->zalloc = nullptr; z_ptr->zfree = nullptr; z_ptr->opaque = nullptr; x_compressInit = & wrapperlib::z_compressInit; x_decompressInit = & wrapperlib::z_decompressInit; x_compressEnd = & wrapperlib::z_compressEnd; x_decompressEnd = & wrapperlib::z_decompressEnd; x_compress = & wrapperlib::z_compress; x_decompress = & wrapperlib::z_decompress; x_set_next_in = & wrapperlib::z_set_next_in; x_set_avail_in = & wrapperlib::z_set_avail_in; x_get_avail_in = & wrapperlib::z_get_avail_in; x_get_total_in = & wrapperlib::z_get_total_in; x_set_next_out = & wrapperlib::z_set_next_out; x_get_next_out = & wrapperlib::z_get_next_out; x_set_avail_out = & wrapperlib::z_set_avail_out; x_get_avail_out = & wrapperlib::z_get_avail_out; x_get_total_out = & wrapperlib::z_get_total_out; break; #else throw Ecompilation("gzip compression support (libz)"); #endif case bzlib_mode: #if LIBBZ2_AVAILABLE bz_ptr = new (nothrow) bz_stream; if(bz_ptr == nullptr) throw Ememory("wrapperlib::wrapperlib"); #if LIBZ_AVAILABLE z_ptr = nullptr; #endif #if LIBLZMA_AVAILABLE lzma_ptr = nullptr; #endif bz_ptr->bzalloc = nullptr; bz_ptr->bzfree = nullptr; bz_ptr->opaque = nullptr; x_compressInit = & wrapperlib::bz_compressInit; x_decompressInit = & wrapperlib::bz_decompressInit; x_compressEnd = & wrapperlib::bz_compressEnd; x_decompressEnd = & wrapperlib::bz_decompressEnd; x_compress = & wrapperlib::bz_compress; x_decompress = & wrapperlib::bz_decompress; x_set_next_in = & wrapperlib::bz_set_next_in; x_set_avail_in = & wrapperlib::bz_set_avail_in; x_get_avail_in = & wrapperlib::bz_get_avail_in; x_get_total_in = & wrapperlib::bz_get_total_in; x_set_next_out = & wrapperlib::bz_set_next_out; x_get_next_out = & wrapperlib::bz_get_next_out; x_set_avail_out = & wrapperlib::bz_set_avail_out; x_get_avail_out = & wrapperlib::bz_get_avail_out; x_get_total_out = & wrapperlib::bz_get_total_out; break; #else throw Ecompilation("bzip2 compression support (libbz2)"); #endif case xz_mode: #if LIBLZMA_AVAILABLE #if LIBZ_AVAILABLE z_ptr = nullptr; #endif #if LIBBZ2_AVAILABLE bz_ptr = nullptr; #endif lzma_ptr = new (nothrow) lzma_stream; if(lzma_ptr == nullptr) throw Ememory("wrapperlib::wrapperlib"); *lzma_ptr = LZMA_STREAM_INIT; x_compressInit = & wrapperlib::lzma_compressInit; x_decompressInit = & wrapperlib::lzma_decompressInit; x_compressEnd = & wrapperlib::lzma_end; x_decompressEnd = & wrapperlib::lzma_end; x_compress = & wrapperlib::lzma_encode; x_decompress = & wrapperlib::lzma_encode; x_set_next_in = & wrapperlib::lzma_set_next_in; x_set_avail_in = & wrapperlib::lzma_set_avail_in; x_get_avail_in = & wrapperlib::lzma_get_avail_in; x_get_total_in = & wrapperlib::lzma_get_total_in; x_set_next_out = & wrapperlib::lzma_set_next_out; x_get_next_out = & wrapperlib::lzma_get_next_out; x_set_avail_out = & wrapperlib::lzma_set_avail_out; x_get_avail_out = & wrapperlib::lzma_get_avail_out; x_get_total_out = & wrapperlib::lzma_get_total_out; #else throw Ecompilation("xz compression support (libxz)"); #endif break; default: throw SRC_BUG; } level = -1; } wrapperlib::~wrapperlib() { #if LIBZ_AVAILABLE if(z_ptr != nullptr) delete z_ptr; #endif #if LIBBZ2_AVAILABLE if(bz_ptr != nullptr) delete bz_ptr; #endif #if LIBLZMA_AVAILABLE if(lzma_ptr != nullptr) { ::lzma_end(lzma_ptr); delete lzma_ptr; } #endif } ////////////// Zlib routines ///////////// #if LIBZ_AVAILABLE S_I wrapperlib::z_compressInit(U_I compression_level) { CHECK_Z; return zlib2wrap_code(deflateInit(z_ptr, compression_level)); } S_I wrapperlib::z_decompressInit() { CHECK_Z; return zlib2wrap_code(inflateInit(z_ptr)); } S_I wrapperlib::z_compressEnd() { CHECK_Z; return zlib2wrap_code(deflateEnd(z_ptr)); } S_I wrapperlib::z_decompressEnd() { CHECK_Z; return zlib2wrap_code(inflateEnd(z_ptr)); } S_I wrapperlib::z_compress(S_I flag) { CHECK_Z; return zlib2wrap_code(deflate(z_ptr, wrap2zlib_code(flag))); } S_I wrapperlib::z_decompress(S_I flag) { CHECK_Z; return zlib2wrap_code(inflate(z_ptr, wrap2zlib_code(flag))); } void wrapperlib::z_set_next_in(const char *x) { CHECK_Z; z_ptr->next_in = (Bytef *)x; } void wrapperlib::z_set_avail_in(U_I x) { CHECK_Z; z_ptr->avail_in = x; } U_I wrapperlib::z_get_avail_in() const { CHECK_Z; return z_ptr->avail_in; } U_64 wrapperlib::z_get_total_in() const { CHECK_Z; return z_ptr->total_in; } void wrapperlib::z_set_next_out(char *x) { CHECK_Z; z_ptr->next_out = (Bytef *)x; } char *wrapperlib::z_get_next_out() const { CHECK_Z; return (char *)z_ptr->next_out; } void wrapperlib::z_set_avail_out(U_I x) { CHECK_Z; z_ptr->avail_out = x; } U_I wrapperlib::z_get_avail_out() const { CHECK_Z; return z_ptr->avail_out; } U_64 wrapperlib::z_get_total_out() const { CHECK_Z; return z_ptr->total_out; } #endif ////////////// BZlib routines ///////////// #if LIBBZ2_AVAILABLE void wrapperlib::bz_set_next_in(const char *x) { CHECK_BZ; bz_ptr->next_in = (char*)x; // It must be a bug in bz that the input is not a const char* } void wrapperlib::bz_set_avail_in(U_I x) { CHECK_BZ; bz_ptr->avail_in = x; } U_I wrapperlib::bz_get_avail_in() const { CHECK_BZ; return bz_ptr->avail_in; } U_64 wrapperlib::bz_get_total_in() const { CHECK_BZ; return ((U_64)(bz_ptr->total_in_hi32) << 32) | ((U_64)(bz_ptr->total_in_lo32)); } void wrapperlib::bz_set_next_out(char *x) { CHECK_BZ; bz_ptr->next_out = x; } char *wrapperlib::bz_get_next_out() const { CHECK_BZ; return bz_ptr->next_out; } void wrapperlib::bz_set_avail_out(U_I x) { CHECK_BZ; bz_ptr->avail_out = x; } U_I wrapperlib::bz_get_avail_out() const { CHECK_BZ; return bz_ptr->avail_out; } U_64 wrapperlib::bz_get_total_out() const { CHECK_BZ; return ((U_64)(bz_ptr->total_out_hi32) << 32) | ((U_64)(bz_ptr->total_out_lo32)); } S_I wrapperlib::bz_compressInit(U_I compression_level) { CHECK_BZ; return bzlib2wrap_code(BZ2_bzCompressInit(bz_ptr, compression_level, 0, 30)); } S_I wrapperlib::bz_decompressInit() { CHECK_BZ; return bzlib2wrap_code(BZ2_bzDecompressInit(bz_ptr, 0,0)); } S_I wrapperlib::bz_compressEnd() { CHECK_BZ; return bzlib2wrap_code(BZ2_bzCompressEnd(bz_ptr)); } S_I wrapperlib::bz_decompressEnd() { CHECK_BZ; return bzlib2wrap_code(BZ2_bzDecompressEnd(bz_ptr)); } S_I wrapperlib::bz_compress(S_I flag) { S_I ret; CHECK_BZ; ret = BZ2_bzCompress(bz_ptr, wrap2bzlib_code(flag)); if(ret == BZ_SEQUENCE_ERROR) ret = BZ_STREAM_END; return bzlib2wrap_code(ret); } S_I wrapperlib::bz_decompress(S_I flag) { // flag is not used here. S_I ret; CHECK_BZ; ret = BZ2_bzDecompress(bz_ptr); if(ret == BZ_SEQUENCE_ERROR) ret = BZ_STREAM_END; return bzlib2wrap_code(ret); } #endif ////////////// LZMA routines ///////////// #if LIBLZMA_AVAILABLE S_I wrapperlib::lzma_compressInit(U_I compression_level) { CHECK_LZMA; return lzma2wrap_code(lzma_easy_encoder(lzma_ptr, compression_level, LZMA_CHECK_CRC32)); // CR32 is large enough, even no LZMA_CHECK_NONE would // be possible as compressed data is protected by libdar // CRC which width is proportionnal to the size of the // compressed file. } S_I wrapperlib::lzma_decompressInit() { CHECK_LZMA; return lzma2wrap_code(lzma_auto_decoder(lzma_ptr, UINT64_MAX, 0)); } S_I wrapperlib::lzma_end() { CHECK_LZMA; return WR_OK; // nothing done } S_I wrapperlib::lzma_encode(S_I flag) { CHECK_LZMA; return lzma2wrap_code(lzma_code(lzma_ptr, wrap2lzma_code(flag))); } void wrapperlib::lzma_set_next_in(const char *x) { CHECK_LZMA; lzma_ptr->next_in = (Bytef *)x; } void wrapperlib::lzma_set_avail_in(U_I x) { CHECK_LZMA; lzma_ptr->avail_in = x; } U_I wrapperlib::lzma_get_avail_in() const { CHECK_LZMA; return lzma_ptr->avail_in; } U_64 wrapperlib::lzma_get_total_in() const { CHECK_LZMA; return lzma_ptr->total_in; } void wrapperlib::lzma_set_next_out(char *x) { CHECK_LZMA; lzma_ptr->next_out = (Bytef *)x; } char *wrapperlib::lzma_get_next_out() const { CHECK_LZMA; return (char *)lzma_ptr->next_out; } void wrapperlib::lzma_set_avail_out(U_I x) { CHECK_LZMA; lzma_ptr->avail_out = x; } U_I wrapperlib::lzma_get_avail_out() const { CHECK_LZMA; return lzma_ptr->avail_out; } U_64 wrapperlib::lzma_get_total_out() const { CHECK_LZMA; return lzma_ptr->total_out; } #endif S_I wrapperlib::compressReset() { S_I ret; if(level < 0) throw Erange("wrapperlib::compressReset", gettext("compressReset called but compressInit never called before")); ret = compressEnd(); if(ret == WR_OK) return compressInit(level); else return ret; } S_I wrapperlib::decompressReset() { S_I ret = decompressEnd(); if(ret == WR_OK) return decompressInit(); else return ret; } #if LIBZ_AVAILABLE static S_I zlib2wrap_code(S_I code) { switch(code) { case Z_OK: return WR_OK; case Z_MEM_ERROR: return WR_MEM_ERROR; case Z_VERSION_ERROR: return WR_VERSION_ERROR; case Z_STREAM_END: return WR_STREAM_END; case Z_DATA_ERROR: return WR_DATA_ERROR; case Z_BUF_ERROR: return WR_BUF_ERROR; case Z_STREAM_ERROR: return WR_STREAM_ERROR; case Z_NEED_DICT: return WR_DATA_ERROR; // we do not use explicit dictionnary for compression, // this is zlib assumes it requires a dictionnary, this is // to be considered a data error. default: throw SRC_BUG; // unexpected error code } } static S_I wrap2zlib_code(S_I code) { switch(code) { case WR_NO_FLUSH: return Z_NO_FLUSH; case WR_FINISH: return Z_FINISH; default: throw SRC_BUG; } } #endif #if LIBBZ2_AVAILABLE static S_I bzlib2wrap_code(S_I code) { switch(code) { case BZ_OK: case BZ_RUN_OK: case BZ_FLUSH_OK: case BZ_FINISH_OK: return WR_OK; case BZ_PARAM_ERROR: return WR_STREAM_ERROR; case BZ_CONFIG_ERROR: return WR_VERSION_ERROR; case BZ_MEM_ERROR: return WR_MEM_ERROR; case BZ_DATA_ERROR: case BZ_DATA_ERROR_MAGIC: return WR_DATA_ERROR; case BZ_STREAM_END: return WR_STREAM_END; case BZ_SEQUENCE_ERROR: default: throw SRC_BUG; } } static S_I wrap2bzlib_code(S_I code) { switch(code) { case WR_NO_FLUSH: return BZ_RUN; case WR_FINISH: return BZ_FINISH; default: throw SRC_BUG; } } #endif #if LIBLZMA_AVAILABLE static S_I lzma2wrap_code(S_I code) { switch(code) { case LZMA_OK: return WR_OK; case LZMA_MEM_ERROR: return WR_MEM_ERROR; case LZMA_OPTIONS_ERROR: return WR_VERSION_ERROR; case LZMA_FORMAT_ERROR: // no memory usage limit used from libdar, only file format error can generate this code return WR_DATA_ERROR; case LZMA_STREAM_END: return WR_STREAM_END; case LZMA_DATA_ERROR: return WR_DATA_ERROR; case LZMA_BUF_ERROR: return WR_BUF_ERROR; case LZMA_NO_CHECK: case LZMA_UNSUPPORTED_CHECK: return WR_STREAM_ERROR; case LZMA_PROG_ERROR: throw SRC_BUG; // error in libdar calling liblzma case LZMA_GET_CHECK: throw SRC_BUG; // can be retured by lzma_code "only if the decoder was initialized with the LZMA_TELL_ANY_CHECK flag" // flag we do not use from libdar default: throw SRC_BUG; // unexpected error code } } static lzma_action wrap2lzma_code(S_I code) { switch(code) { case WR_NO_FLUSH: return LZMA_RUN; case WR_FINISH: return LZMA_FINISH; default: throw SRC_BUG; } } #endif } // end of namespace dar-2.7.15/src/libdar/fsa_family.hpp0000644000175000017500000000510014636066467014162 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file fsa_family.hpp /// \brief filesystem specific attributes available families and fsa_scope definition /// \ingroup API #ifndef FSA_FAMILY_HPP #define FSA_FAMILY_HPP #include #include #include "integers.hpp" namespace libdar { /// \addtogroup API /// @{ /// FSA family enum fsa_family { fsaf_hfs_plus, fsaf_linux_extX }; // note: adding new fsa_family need updating all_fsa_family() /// FSA nature enum fsa_nature { fsan_unset, fsan_creation_date, fsan_append_only, fsan_compressed, fsan_no_dump, fsan_immutable, fsan_data_journaling, fsan_secure_deletion, fsan_no_tail_merging, fsan_undeletable, fsan_noatime_update, fsan_synchronous_directory, fsan_synchronous_update, fsan_top_of_dir_hierarchy }; /// convert fsa family to readable std::string extern std::string fsa_family_to_string(fsa_family f); /// convert fsa nature to readable std::string extern std::string fsa_nature_to_string(fsa_nature n); /// set of fsa families using fsa_scope = std::set; /// provides a scope containing all FSA families extern fsa_scope all_fsa_families(); /// convert an fsa scope to infinint extern infinint fsa_scope_to_infinint(const fsa_scope & val); /// convert an infinint to fsa_scape extern fsa_scope infinint_to_fsa_scope(const infinint & ref); /// convert an fsa scope to readable string extern std::string fsa_scope_to_string(bool saved, const fsa_scope & scope); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_signature.cpp0000644000175000017500000000652314636066467014705 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_signature.hpp" using namespace std; namespace libdar { cat_signature::cat_signature(unsigned char original, saved_status status) { if(!islower(original)) throw SRC_BUG; switch(status) { case saved_status::saved: field = 3; break; case saved_status::inode_only: field = 4; break; case saved_status::fake: field = 7; break; case saved_status::not_saved: field = 2; break; case saved_status::delta: field = 1; break; default: throw SRC_BUG; } field <<= 5; field |= (original & 0x1F); // only adding the 5 lower bits of "original" field } cat_signature::cat_signature(generic_file & f, const archive_version & reading_ver) { unsigned char tmp_base; saved_status tmp_status; if(!read(f, reading_ver) || !get_base_and_status(tmp_base, tmp_status)) throw Erange("cat_signature::cat_signature(generic_file)", gettext("incoherent catalogue structure")); } bool cat_signature::read(generic_file & f, const archive_version & reading_ver) { return f.read((char *)&field, 1) == 1; } void cat_signature::write(generic_file &f) { f.write((const char *)&field, 1); } bool cat_signature::get_base_and_status(unsigned char & base, saved_status & saved) const { // building the lowercase letter by forcing the bits 6, 7 and 8 to the value 011: // 0x1F is 0001 1111 in binary // 0x60 is 0110 0000 in binary base = ((field & 0x1F) | 0x60); if(!islower(base)) return false; // must be a letter U_I val = field >> 5; switch(val) { case 0: return false; case 1: saved = saved_status::delta; break; case 2: saved = saved_status::not_saved; break; case 3: saved = saved_status::saved; break; case 4: saved = saved_status::inode_only; break; case 5: case 6: return false; case 7: saved = saved_status::fake; break; default: throw SRC_BUG; } return true; } bool cat_signature::compatible_signature(unsigned char a, unsigned char b) { switch(a) { case 'e': case 'f': return b == 'e' || b == 'f'; default: return b == a; } } } // end of namespace dar-2.7.15/src/libdar/crypto_sym.cpp0000644000175000017500000007246414636066467014274 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ARGON2_H #include #endif } #include "crypto_sym.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "elastic.hpp" using namespace std; namespace libdar { constexpr const U_I MAX_RETRY_IF_WEAK_PASSWORD = 5; crypto_sym::crypto_sym(const secu_string & password, const archive_version & reading_version, crypto_algo xalgo, const std::string & salt, const infinint & iteration_count, hash_algo kdf_hash, bool use_pkcs5) { #if CRYPTO_AVAILABLE U_I algo_id; S_I retry = use_pkcs5? MAX_RETRY_IF_WEAK_PASSWORD: 0; main_clef = nullptr; essiv_clef = nullptr; ivec = nullptr; try { reading_ver = reading_version; algo = xalgo; algo_id = get_algo_id(algo); if(reading_ver <= 5 && algo == crypto_algo::blowfish) throw Erange("crypto_sym::crypto_sym", gettext("Current implementation of blowfish encryption is not compatible with old (weak) implementation, use dar-2.3.x software or later (or other software based on libdar-4.4.x or greater) to read this archive")); if(kdf_hash == hash_algo::none && use_pkcs5) throw Erange("crypto_sym::crypto_sym", gettext("cannot use 'none' as hashing algorithm for key derivation function")); // checking for algorithm availability in libgcrypt gcry_error_t err = gcry_cipher_algo_info(algo_id, GCRYCTL_TEST_ALGO, nullptr, nullptr); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::crypto_sym",tools_printf(gettext("Cyphering algorithm not available in libgcrypt: %s/%s"), gcry_strsource(err),gcry_strerror(err))); do { // generate a salt if not provided and pkcs5 is needed if(salt.empty() && use_pkcs5 && reading_version >= 10) sel = generate_salt(max_key_len(xalgo)); else sel = salt; // building the hashed_password init_hashed_password(password, use_pkcs5, sel, iteration_count, kdf_hash, algo); } while(! is_a_strong_password(algo, hashed_password) && --retry >= 0); if(retry < 0) throw Erange("crypto_sym::crypto_sym", tools_printf(gettext("Failed to obtain a strong hashed password after %d retries with pkcs5 and different salt values, aborting"), MAX_RETRY_IF_WEAK_PASSWORD)); // building the main key for this object init_main_clef(hashed_password, algo); // building the Initial Vector structure that will be // used with the main key init_algo_block_size(algo); init_ivec(algo, algo_block_size); U_I IV_cipher; U_I IV_hashing; get_IV_cipher_and_hashing(reading_ver, algo_id, IV_cipher, IV_hashing); // making an hash of the provided password into the digest variable init_essiv_password(hashed_password, IV_hashing); // building the auxilliary key that will be used // to derive the IV value from the block number init_essiv_clef(essiv_password, IV_cipher, algo_block_size); #ifdef LIBDAR_NO_OPTIMIZATION self_test(); #endif } catch(...) { detruit(); throw; } #else throw Ecompilation(gettext("Strong encryption support (libgcrypt)")); #endif } U_32 crypto_sym::encrypted_block_size_for(U_32 clear_block_size) { #if CRYPTO_AVAILABLE return ((clear_block_size / algo_block_size) + 1) * algo_block_size; // round to the upper "algo_block_size" byte block of data. // and add an additional "algo_block_size" block if no rounding is necessary. // (we need some place to add the elastic buffer at the end of the block) #else throw Ecompilation(gettext("Strong encryption support (libgcrypt)")); #endif } U_32 crypto_sym::clear_block_allocated_size_for(U_32 clear_block_size) { return encrypted_block_size_for(clear_block_size); } U_32 crypto_sym::encrypt_data(const infinint & block_num, const char *clear_buf, const U_32 clear_size, const U_32 clear_allocated, char *crypt_buf, U_32 crypt_size) { #if CRYPTO_AVAILABLE U_32 size_to_fill = encrypted_block_size_for(clear_size); // sanity checks // if(crypt_size < size_to_fill) throw SRC_BUG; // not enough room to write encrypted data if(clear_allocated < size_to_fill) // note : clear_block_size_for() returns the same as encrypted_block_size_for() throw SRC_BUG; // not large enough allocated memory in clear buffer to add padding // // end of sanity checks if(clear_size < size_to_fill) { elastic stic = elastic(size_to_fill - clear_size); gcry_error_t err; stic.dump((unsigned char *)(const_cast(clear_buf + clear_size)), (U_32)(clear_allocated - clear_size)); err = gcry_cipher_reset(main_clef); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::encrypt_data",tools_printf(gettext("Error while resetting encryption key for a new block: %s/%s"), gcry_strsource(err),gcry_strerror(err))); make_ivec(block_num, ivec, algo_block_size, essiv_clef); err = gcry_cipher_setiv(main_clef, (const void *)ivec, algo_block_size); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::encrypt_data",tools_printf(gettext("Error while setting IV for current block: %s/%s"), gcry_strsource(err),gcry_strerror(err))); err = gcry_cipher_encrypt(main_clef, (unsigned char *)crypt_buf, size_to_fill, (const unsigned char *)clear_buf, size_to_fill); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::encrypt_data",tools_printf(gettext("Error while cyphering data: %s/%s"), gcry_strsource(err),gcry_strerror(err))); return size_to_fill; } else throw SRC_BUG; #else throw Ecompilation(gettext("Strong encryption support (libgcrypt)")); #endif } U_32 crypto_sym::decrypt_data(const infinint & block_num, const char *crypt_buf, const U_32 crypt_size, char *clear_buf, U_32 clear_size) { #if CRYPTO_AVAILABLE gcry_error_t err; if(crypt_size == 0) return 0; // nothing to decipher make_ivec(block_num, ivec, algo_block_size, essiv_clef); err = gcry_cipher_setiv(main_clef, (const void *)ivec, algo_block_size); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::decrypt_data",tools_printf(gettext("Error while setting IV for current block: %s/%s"), gcry_strsource(err),gcry_strerror(err))); err = gcry_cipher_decrypt(main_clef, (unsigned char *)clear_buf, clear_size, (const unsigned char *)crypt_buf, crypt_size); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::decrypt_data",tools_printf(gettext("Error while decyphering data: %s/%s"), gcry_strsource(err),gcry_strerror(err))); elastic stoc = elastic((unsigned char *)clear_buf, crypt_size, elastic_backward, reading_ver); if(stoc.get_size() > crypt_size) throw Erange("crypto_sym::decrypt_data",gettext("Data corruption may have occurred, cannot decrypt data")); return crypt_size - stoc.get_size(); // this is crypt_size to be used here as the clear data has the same length // gcry_cipher_decrypt does not provide any mean to know the amount of clear bytes produced #else throw Ecompilation(gettext("Strong encryption support (libgcrypt)")); #endif } std::unique_ptr crypto_sym::clone() const { try { return std::make_unique(*this); } catch(bad_alloc &) { throw Ememory("crypto_sym::clone"); } } size_t crypto_sym::max_key_len(crypto_algo algo) { #if CRYPTO_AVAILABLE size_t key_len; U_I algo_id = get_algo_id(algo); gcry_error_t err; // checking for algorithm availability err = gcry_cipher_algo_info(algo_id, GCRYCTL_TEST_ALGO, nullptr, nullptr); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::max_key_len",tools_printf(gettext("Cyphering algorithm not available in libgcrypt: %s/%s"), gcry_strsource(err),gcry_strerror(err))); // obtaining the maximum key length key_len = gcry_cipher_get_algo_keylen(algo_id); if(key_len == 0) throw Erange("crypto_sym::max_key_len",gettext("Failed retrieving from libgcrypt the maximum key length")); return key_len; #else throw Ecompilation("Strong encryption support (libgcrypt)"); #endif } size_t crypto_sym::max_key_len_libdar(crypto_algo algo) { #if CRYPTO_AVAILABLE size_t key_len = max_key_len(algo); if(algo == crypto_algo::blowfish) key_len = 56; // for historical reasons return key_len; #else throw Ecompilation("Strong encryption support (libgcrypt)"); #endif } bool crypto_sym::is_a_strong_password(crypto_algo algo, const secu_string & password) { #if CRYPTO_AVAILABLE bool ret = true; gcry_error_t err; gcry_cipher_hd_t main_clef; U_I algo_id = get_algo_id(algo); err = gcry_cipher_open(&main_clef, algo_id, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_SECURE); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::is_a_strong_password",tools_printf(gettext("Error while opening libgcrypt key handle to check password strength: %s/%s"), gcry_strsource(err), gcry_strerror(err))); try { err = gcry_cipher_setkey(main_clef, (const void *)password.c_str(), password.get_size()); if(err != GPG_ERR_NO_ERROR) { if(gcry_err_code(err) == GPG_ERR_WEAK_KEY) ret = false; else throw Erange("crypto_sym::is_a_strong_password",tools_printf(gettext("Error while assigning key to libgcrypt key handle to check password strength: %s/%s"), gcry_strsource(err),gcry_strerror(err))); } } catch(...) { gcry_cipher_close(main_clef); throw; } gcry_cipher_close(main_clef); return ret; #else throw Ecompilation("Strong encryption support (libgcrypt)"); #endif } #if CRYPTO_AVAILABLE string crypto_sym::generate_salt(U_I size) { string ret; unsigned char* buffer = new (nothrow) unsigned char[size]; if(buffer == nullptr) throw Ememory("crypto_sym::generate_salt"); try { gcry_create_nonce(buffer, size); ret.assign((const char *)buffer, size); delete [] buffer; buffer = nullptr; } catch(...) { if(buffer != nullptr) { delete [] buffer; buffer = nullptr; } throw; } return ret; } void crypto_sym::init_hashed_password(const secu_string & password, bool use_pkcs5, const std::string & salt, infinint iteration_count, hash_algo kdf_hash, crypto_algo algo) { if(use_pkcs5) { U_I it = 0; iteration_count.unstack(it); if(!iteration_count.is_zero()) throw Erange("crypto_sym::init_hashed_password", gettext("Too large value give for key derivation interation count")); switch(kdf_hash) { case hash_algo::none: throw SRC_BUG; case hash_algo::md5: case hash_algo::sha1: case hash_algo::sha512: hashed_password = pkcs5_pass2key(password, salt, it, hash_algo_to_gcrypt_hash(kdf_hash), max_key_len_libdar(algo)); break; case hash_algo::argon2: hashed_password = argon2_pass2key(password, salt, it, max_key_len_libdar(algo)); break; default: throw SRC_BUG; } } else hashed_password = password; } void crypto_sym::init_essiv_password(const secu_string & key, unsigned int IV_hashing) { U_I digest_len = gcry_md_get_algo_dlen(IV_hashing); if(digest_len == 0) throw SRC_BUG; essiv_password.resize(digest_len); essiv_password.expand_string_size_to(digest_len); // making a hash of the provided password into the digest variable gcry_md_hash_buffer(IV_hashing, essiv_password.get_array(), (const void *)key.c_str(), key.get_size()); } void crypto_sym::init_main_clef(const secu_string & password, crypto_algo algo) { try { gcry_error_t err; // key handle initialization err = gcry_cipher_open(&main_clef, get_algo_id(algo), GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_SECURE); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::init_main_clef",tools_printf(gettext("Error while opening libgcrypt key handle: %s/%s"), gcry_strsource(err), gcry_strerror(err))); // assigning key to the handle err = gcry_cipher_setkey(main_clef, (const void *)hashed_password.c_str(), hashed_password.get_size()); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::init_main_clef",tools_printf(gettext("Error while assigning key to libgcrypt key handle: %s/%s"), gcry_strsource(err),gcry_strerror(err))); } catch(...) { detruit(); throw; }; } void crypto_sym::init_essiv_clef(const secu_string & essiv_password, U_I IV_cipher, U_I main_cipher_algo_block_size) { gcry_error_t err; // creating a handle for a new handle with algo equal to "IV_cipher" err = gcry_cipher_open(&essiv_clef, IV_cipher, GCRY_CIPHER_MODE_ECB, GCRY_CIPHER_SECURE); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::init_essiv_clef",tools_printf(gettext("Error while creating ESSIV handle: %s/%s"), gcry_strsource(err),gcry_strerror(err))); // obtaining key len for IV_cipher size_t essiv_key_len; err = gcry_cipher_algo_info(IV_cipher, GCRYCTL_GET_KEYLEN, nullptr, &essiv_key_len); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::init_essiv_clef", tools_printf(gettext("Error while setting IV for current block: %s/%s"), gcry_strsource(err),gcry_strerror(err))); // failing if the digest size is larger than key size for IV_cipher if(essiv_password.get_size() > essiv_key_len && IV_cipher != GCRY_CIPHER_BLOWFISH) throw SRC_BUG; // IV_cipher and IV_hashing must be chosen in coherence! // we do not complain for older format archive... its done now and worked so far. // This bug report is rather to signal possible problem when time will come // to update the cipher and digest algorithms // assiging the essiv_password to the new handle err = gcry_cipher_setkey(essiv_clef, essiv_password.c_str(), essiv_password.get_size()); if(err != GPG_ERR_NO_ERROR) { // tolerating WEAK key here, we use that key to fill the IV of the real strong key // (the encrypted data by this key --- the IV values for each block --- are not stored in the archive) if(gpg_err_code(err) != GPG_ERR_WEAK_KEY) throw Erange("crypto_sym::init_essiv_clef",tools_printf(gettext("Error while assigning key to libgcrypt key handle (essiv): %s/%s"), gcry_strsource(err),gcry_strerror(err))); } // obtaining the block size of the essiv cipher size_t algo_block_size_essiv; err = gcry_cipher_algo_info(IV_cipher, GCRYCTL_GET_BLKLEN, nullptr, &algo_block_size_essiv); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::init_essiv_clef",tools_printf(gettext("Failed retrieving from libgcrypt the block size used by the cyphering algorithm (essiv): %s/%s"), gcry_strsource(err),gcry_strerror(err))); if(algo_block_size_essiv == 0) throw SRC_BUG; if(main_cipher_algo_block_size == 0) throw SRC_BUG; // testing the coherence of block size between main cipher key // and cipher key used to generate IV for the main cipher key if(main_cipher_algo_block_size < algo_block_size_essiv) throw SRC_BUG; // cannot cipher less data (IV for main key) than block size of IV key else if(main_cipher_algo_block_size % algo_block_size_essiv != 0) throw SRC_BUG; // in ECB mode we should encrypt an integer number of blocks according // to IV key block size } void crypto_sym::init_algo_block_size(crypto_algo algo) { gcry_error_t err; // obtaining the block length err = gcry_cipher_algo_info(get_algo_id(algo), GCRYCTL_GET_BLKLEN, nullptr, &algo_block_size); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::init_algo_block_size",tools_printf(gettext("Failed retrieving from libgcrypt the block size used by the cyphering algorithm: %s/%s"), gcry_strsource(err),gcry_strerror(err))); if(algo_block_size == 0) throw SRC_BUG; } void crypto_sym::init_ivec(crypto_algo algo, size_t algo_block_size) { // initializing ivec in secure memory ivec = (unsigned char *)gcry_malloc_secure(algo_block_size); if(ivec == nullptr) throw Esecu_memory("crypto_sym::init_ivec"); } void crypto_sym::detruit() { if(main_clef != nullptr) gcry_cipher_close(main_clef); if(essiv_clef != nullptr) gcry_cipher_close(essiv_clef); if(ivec != nullptr) { (void)memset(ivec, 0, algo_block_size); gcry_free(ivec); } } void crypto_sym::copy_from(const crypto_sym & ref) { reading_ver = ref.reading_ver; algo = ref.algo; hashed_password = ref.hashed_password; essiv_password = ref.essiv_password; init_main_clef(hashed_password, algo); init_algo_block_size(algo); init_ivec(algo, algo_block_size); U_I IV_cipher; U_I IV_hashing; get_IV_cipher_and_hashing(reading_ver, get_algo_id(algo), IV_cipher, IV_hashing); init_essiv_clef(essiv_password, IV_cipher, algo_block_size); sel = ref.sel; } void crypto_sym::move_from(crypto_sym && ref) { // we assume the current object has no field assigned // same as for copy_from(): reading_ver = move(ref.reading_ver); algo = move(ref.algo); hashed_password = move(ref.hashed_password); essiv_password = move(ref.essiv_password); main_clef = move(ref.main_clef); essiv_clef = move(ref.essiv_clef); algo_block_size = move(ref.algo_block_size); ivec = move(ref.ivec); sel = move(ref.sel); ref.main_clef = nullptr; ref.essiv_clef = nullptr; ivec = nullptr; } void crypto_sym::get_IV_cipher_and_hashing(const archive_version & ver, U_I main_cipher, U_I & cipher, U_I & hashing) { if(ver >= archive_version(8,1) && main_cipher != get_algo_id(crypto_algo::blowfish)) { cipher = GCRY_CIPHER_AES256; // to have an algorithm available when ligcrypt is used in FIPS mode hashing = GCRY_MD_SHA256; // SHA224 was also ok but as time passes, it would get sooner unsave } else { // due to 8 bytes block_size we keep using // blowfish for the essiv key: other cipher have 16 bytes block size // and generating an IV of eight bytes would require doubling the size // of the data to encrypt and skinking the encrypted data afterward to // the 8 requested bytes of the IV for the main key... // // in the other side, the replacement of blowfish by aes256 starting format 8.1 // was requested to have libdar working when libgcrypt is in FIPS mode, which // forbids the use of blowfish... we stay here compatible with FIPS mode. cipher = GCRY_CIPHER_BLOWFISH; hashing = GCRY_MD_SHA1; } } void crypto_sym::make_ivec(const infinint & ref, unsigned char *ivec, U_I size, const gcry_cipher_hd_t & IVkey) { // Stronger IV calculation: ESSIV. // ESSIV mode helps to provide (at least) IND-CPA security. unsigned char *sect = nullptr; infinint ref_cp = ref; infinint tmp; gcry_error_t err; sect = new (nothrow) unsigned char[size]; if(sect == nullptr) throw Ememory("crypto_sym::make_ivec"); try { U_I i = size; while(i > 0) { --i; sect[i] = ref_cp[0]; ref_cp >>= 8; } // IV(sector) = E_salt(sector) err = gcry_cipher_encrypt(IVkey, (unsigned char *)ivec, size, (const unsigned char *)sect, size); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::make_ivec",tools_printf(gettext("Error while generating IV: %s/%s"), gcry_strsource(err), gcry_strerror(err))); } catch(...) { delete [] sect; throw; } delete [] sect; } secu_string crypto_sym::pkcs5_pass2key(const secu_string & password, const string & salt, U_I iteration_count, U_I hash_gcrypt, U_I output_length) { // Password-based key derivation function (PBKDF2) from PKCS#5 v2.0 // Using HMAC-SHA1 as the underlying pseudorandom function. gcry_error_t err; gcry_md_hd_t hmac; U_32 l = 0, r = 0; secu_string retval; if (output_length == 0) return secu_string(); // Let l be the number of EVP_MD_size(digest) blocks in the derived key, rounding up. // Let r be the number of octets in the last block. l = output_length / gcry_md_get_algo_dlen(hash_gcrypt); r = output_length % gcry_md_get_algo_dlen(hash_gcrypt); if (r == 0) r = gcry_md_get_algo_dlen(hash_gcrypt); else ++l; // round up // testing SHA1 availability err = gcry_md_test_algo(hash_gcrypt); if(err != GPG_ERR_NO_ERROR) throw Ecompilation(tools_printf(gettext("Error! SHA1 not available in libgcrypt: %s/%s"), gcry_strsource(err),gcry_strerror(err))); // opening a handle for Message Digest err = gcry_md_open(&hmac, hash_gcrypt, GCRY_MD_FLAG_SECURE|GCRY_MD_FLAG_HMAC); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::pkcs5_pass2key",tools_printf(gettext("Error while derivating key from password (HMAC open): %s/%s"), gcry_strsource(err),gcry_strerror(err))); // setting the HMAC key err = gcry_md_setkey(hmac, password.c_str(), password.get_size()); if(err != GPG_ERR_NO_ERROR) throw Erange("crypto_sym::pkcs5_pass2key",tools_printf(gettext("Error while derivating key from password (HMAC set key): %s/%s"), gcry_strsource(err),gcry_strerror(err))); // now ready to compute HMAC-SHA1 message digest using "hmac" try { U_I UjLen = gcry_md_get_algo_dlen(hash_gcrypt); char *Ti = nullptr, *Uj = nullptr; retval.resize(output_length); Ti = (char *)gcry_malloc_secure(gcry_md_get_algo_dlen(hash_gcrypt)); if(Ti == nullptr) throw Ememory("crypto_sym::pkcs5_pass2key"); try { Uj = (char *)gcry_malloc_secure(gcry_md_get_algo_dlen(hash_gcrypt)); if(Uj == nullptr) throw Ememory("crypto_sym::pkcs5_pass2key"); try { for (U_32 i = 1; i <= l; ++i) { // Ti = U_1 \xor U_2 \xor ... \xor U_c // U_1 = PRF(P, S || INT(i)) unsigned char ii[4]; unsigned char *tmp_md = nullptr; ii[0] = (i >> 24) & 0xff; ii[1] = (i >> 16) & 0xff; ii[2] = (i >> 8) & 0xff; ii[3] = i & 0xff; gcry_md_reset(hmac); gcry_md_write(hmac, (const unsigned char *) salt.c_str(), salt.size()); gcry_md_write(hmac, ii, 4); tmp_md = gcry_md_read(hmac, hash_gcrypt); (void)memcpy(Uj, tmp_md, gcry_md_get_algo_dlen(hash_gcrypt)); (void)memcpy(Ti, tmp_md, gcry_md_get_algo_dlen(hash_gcrypt)); for (U_32 j = 2; j <= iteration_count; ++j) { // U_j = PRF(P, U_{j-1}) gcry_md_reset(hmac); gcry_md_write(hmac, (const unsigned char *) Uj, UjLen); tmp_md = gcry_md_read(hmac, hash_gcrypt); (void)memcpy(Uj, tmp_md, gcry_md_get_algo_dlen(hash_gcrypt)); tools_memxor(Ti, tmp_md, gcry_md_get_algo_dlen(hash_gcrypt)); } if (i < l) retval.append(Ti, gcry_md_get_algo_dlen(hash_gcrypt)); else // last block retval.append(Ti, r); } // end of main for() loop } catch(...) { (void)memset(Uj, 0, gcry_md_get_algo_dlen(hash_gcrypt)); gcry_free(Uj); throw; } (void)memset(Uj, 0, gcry_md_get_algo_dlen(hash_gcrypt)); gcry_free(Uj); } catch(...) { (void)memset(Ti, 0, gcry_md_get_algo_dlen(hash_gcrypt)); gcry_free(Ti); throw; } (void)memset(Ti, 0, gcry_md_get_algo_dlen(hash_gcrypt)); gcry_free(Ti); } catch(...) { gcry_md_close(hmac); throw; } gcry_md_close(hmac); return retval; } U_I crypto_sym::get_algo_id(crypto_algo algo) { U_I algo_id; switch(algo) { case crypto_algo::blowfish: algo_id = GCRY_CIPHER_BLOWFISH; break; case crypto_algo::aes256: algo_id = GCRY_CIPHER_AES256; break; case crypto_algo::twofish256: algo_id = GCRY_CIPHER_TWOFISH; break; case crypto_algo::serpent256: algo_id = GCRY_CIPHER_SERPENT256; break; case crypto_algo::camellia256: algo_id = GCRY_CIPHER_CAMELLIA256; break; default: throw SRC_BUG; } return algo_id; } secu_string crypto_sym::argon2_pass2key(const secu_string & password, const std::string & salt, U_I iteration_count, U_I output_length) { #if LIBARGON2_AVAILABLE secu_string ret(output_length); S_I err = argon2id_hash_raw(iteration_count, 100, // 100 kiB memory used at max 1, // assuming less parallelization leads to longer hash time and thus more difficult dictionnary attack (?) password.c_str(), password.get_size(), salt.c_str(), salt.size(), ret.c_str(), ret.get_allocated_size()); if(err != ARGON2_OK) { throw Erange("crypto_sym::argon2_pas2key", tools_printf(gettext("Error while computing KDF with argon2 algorithm: %d"), err)); } else ret.set_size(output_length); return ret; #else throw Efeature("libargon2"); #endif } #ifdef LIBDAR_NO_OPTIMIZATION bool crypto_sym::self_tested = false; void crypto_sym::self_test(void) { if(self_tested) return; else self_tested = true; // // Test PBKDF2 (test vectors are from RFC 3962.) // secu_string result; string p1 = "password"; string p2 = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // 64 characters string p4 = p2 + "X"; // 65 characters secu_string pass = secu_string(100); pass.append(p1.c_str(), (U_I)p1.size()); result = pkcs5_pass2key(pass, "ATHENA.MIT.EDUraeburn", 1, GCRY_MD_SHA1, 16); if (result != string("\xcd\xed\xb5\x28\x1b\xb2\xf8\x01\x56\x5a\x11\x22\xb2\x56\x35\x15", 16)) throw Erange("crypto_sym::self_test", gettext("Library used for blowfish encryption does not respect RFC 3962")); result = pkcs5_pass2key(pass, "ATHENA.MIT.EDUraeburn", 1200, GCRY_MD_SHA1, 32); if (result != string("\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b" "\xa7\xe5\x2d\xdb\xc5\xe5\x14\x2f\x70\x8a\x31\xe2\xe6\x2b\x1e\x13", 32) ) throw Erange("crypto_sym::self_test", gettext("Library used for blowfish encryption does not respect RFC 3962")); pass.clear(); pass.append(p2.c_str(), (U_I)p2.size()); result = pkcs5_pass2key(pass, "pass phrase equals block size", 1200, GCRY_MD_SHA1, 32); if (result != string("\x13\x9c\x30\xc0\x96\x6b\xc3\x2b\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9" "\xc5\xec\x59\xf1\xa4\x52\xf5\xcc\x9a\xd9\x40\xfe\xa0\x59\x8e\xd1", 32)) throw Erange("crypto_sym::self_test", gettext("Library used for blowfish encryption does not respect RFC 3962")); pass.resize((U_I)p4.size()); pass.append(p4.c_str(), (U_I)p4.size()); result = pkcs5_pass2key(pass, "pass phrase exceeds block size", 1200, GCRY_MD_SHA1, 32); if (result != string("\x9c\xca\xd6\xd4\x68\x77\x0c\xd5\x1b\x10\xe6\xa6\x87\x21\xbe\x61" "\x1a\x8b\x4d\x28\x26\x01\xdb\x3b\x36\xbe\x92\x46\x91\x5e\xc8\x2a", 32)) throw Erange("crypto_sym::self_test", gettext("Library used for blowfish encryption does not respect RFC 3962")); // // Test make_ivec // struct { unsigned int sector; char iv[9]; } tests[] = { { 0, "\x79\xbf\x81\x22\x26\xe4\x13\x6f" }, { 7, "\x61\x03\xd1\x20\x8a\x0d\x22\x2d" }, { 8, "\xc9\x61\xce\x29\x2e\x65\x28\xbe" }, { 0x10000007, "\x37\xe9\xc0\x92\xc3\x55\xfb\x4b" }, { 0xa5a55a5a, "\x08\x7f\x1a\xa9\xec\x4a\xc0\xc5" }, { 0xffffffff, "\x7a\x8f\x9c\xd0\xcb\xcc\x56\xec" }, { 0xdeadbeef, "" } }; char ivec[8]; int i; string p3 = string("\0\0\0\0", 4); pass.clear(); pass.append(p3.c_str(), (U_I)p3.size()); // temporary object used to build the key to // generate IV crypto_sym tester(pass, 6, crypto_algo::blowfish, "", 0, hash_algo::md5, false); // the following is ugly, we make a reference // in the local block to the field of the tester // object. We can do that because self_test() // is a member (yet a static one) of the crypto_sym // class. // this is done to reduce change in this testing // routine that was getting a gcry_cipher_hd_t key // from another static method of class crpto_sym // in the past. Since then this routine has been // moved and split in several non static methods gcry_cipher_hd_t & esivkey = tester.essiv_clef; for (i = 0; tests[i].sector != 0xdeadbeef; ++i) { make_ivec(tests[i].sector, (unsigned char *) ivec, 8, esivkey); if (memcmp(ivec, tests[i].iv, 8) != 0) //cerr << "sector = " << tests[i].sector << endl; //cerr << "ivec = @@" << string(ivec, 8) << "@@" << endl; //cerr << "should be @@" << string(tests[i].iv, 8) << "@@" << endl; throw Erange("crypto_sym::self_test", gettext("Library used for blowfish encryption does not respect RFC 3962")); } } #endif #endif } // end of namespace dar-2.7.15/src/libdar/entrepot_local.hpp0000644000175000017500000000660614636067146015071 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file entrepot_local.hpp /// \brief defines the implementation for local filesystem entrepot /// The entrepot_local correspond to the local filesystems. /// \ingroup Private #ifndef ENTREPOT_LOCAL_HPP #define ENTREPOT_LOCAL_HPP #include "../my_config.h" #include #include "user_interaction.hpp" #include "entrepot.hpp" #include "fichier_global.hpp" #include "etage.hpp" namespace libdar { /// \addtogroup Private /// @{ /// implementation for entrepot to access to local filesystem /// /// entrepot_local generates objects of class "fichier_local" inherited class of fichier_global class entrepot_local : public entrepot { public: entrepot_local(const std::string & user, const std::string & group, bool x_furtive_mode); entrepot_local(const entrepot_local & ref): entrepot(ref) { copy_from(ref); }; entrepot_local(entrepot_local && ref) noexcept: entrepot(std::move(ref)) { nullifyptr(); move_from(std::move(ref)); }; entrepot_local & operator = (const entrepot_local & ref); entrepot_local & operator = (entrepot_local && ref) noexcept { entrepot::operator = (std::move(ref)); move_from(std::move(ref)); return *this; }; ~entrepot_local() { detruit(); }; virtual std::string get_url() const override { return std::string("file://") + get_full_path().display(); }; virtual void read_dir_reset() const override; virtual bool read_dir_next(std::string & filename) const override; virtual entrepot *clone() const override { return new (std::nothrow) entrepot_local(*this); }; protected: virtual fichier_global *inherited_open(const std::shared_ptr & dialog, const std::string & filename, gf_mode mode, bool force_permission, U_I permission, bool fail_if_exists, bool erase) const override; virtual void inherited_unlink(const std::string & filename) const override; virtual void read_dir_flush() override { detruit(); }; private: bool furtive_mode; etage *contents; void nullifyptr() noexcept { contents = nullptr; }; void copy_from(const entrepot_local & ref) { furtive_mode = ref.furtive_mode; contents = nullptr; }; void move_from(entrepot_local && ref) noexcept { std::swap(contents, ref.contents), std::swap(furtive_mode, ref.furtive_mode); }; void detruit() { if(contents != nullptr) { delete contents; contents = nullptr; } }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/op_tools.hpp0000644000175000017500000000674714636066467013730 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file op_tools.hpp /// \brief contains tools helper for overwriting policy management /// \ingroup Private #ifndef OP_TOOLS_HPP #define OP_TOOLS_HPP #include "../my_config.h" #include #include "crit_action.hpp" #include "cat_entree.hpp" namespace libdar { /// \addtogroup Private /// @{ /// ask user for EA action /// \param[in] dialog for user interaction /// \param[in] full_name full path to the entry do ask decision for /// \param[in] already_here pointer to the object 'in place' /// \param[in] dolly pointer to the object 'to be added' /// \return the action decided by the user. The user may also choose to abort, which will throw an Euser_abort exception extern over_action_ea op_tools_crit_ask_user_for_EA_action(user_interaction & dialog, const std::string & full_name, const cat_entree *already_here, const cat_entree *dolly); /// ask user for FSA action /// \param[in] dialog for user interaction /// \param[in] full_name full path to the entry do ask decision for /// \param[in] already_here pointer to the object 'in place' /// \param[in] dolly pointer to the object 'to be added' /// \return the action decided by the user. The user may also choose to abort, which will throw an Euser_abort exception extern over_action_ea op_tools_crit_ask_user_for_FSA_action(user_interaction & dialog, const std::string & full_name, const cat_entree *already_here, const cat_entree *dolly); /// ask user for Data action /// \param[in] dialog for user interaction /// \param[in] full_name full path to the entry do ask decision for /// \param[in] already_here pointer to the object 'in place' /// \param[in] dolly pointer to the object 'to be added' /// \return the action decided by the user. The user may also choose to abort, which will throw an Euser_abort exception extern over_action_data op_tools_crit_ask_user_for_data_action(user_interaction & dialog, const std::string & full_name, const cat_entree *already_here, const cat_entree *dolly); /// show information suited for user comparison and decision for entry in conflict /// \param[in] dialog for user interaction /// \param[in] full_name path to the entry of the entry to display information /// \param[in] already_here pointer to the object 'in place' /// \param[in] dolly pointer to the object 'to be added' extern void op_tools_crit_show_entry_info(user_interaction & dialog, const std::string & full_name, const cat_entree *already_here, const cat_entree *dolly); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/pile.cpp0000644000175000017500000002264514636066467013011 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "pile.hpp" #include using namespace std; namespace libdar { void pile::push(generic_file *f, const string & label, bool extend_mode) { face to_add; if(is_terminated()) throw SRC_BUG; if(f == nullptr) throw SRC_BUG; if(look_for_label(label) != stack.end()) throw Erange("pile::push", "Label already used while pushing a generic_file on a stack"); if(stack.empty()) set_mode(f->get_mode()); if(f->get_mode() != get_mode() // not the exact same mode && (!extend_mode || f->get_mode() != gf_read_write) // not extending the mode && get_mode() != gf_read_write) // not reducing the mode throw Erange("pile::push", "Adding to the stack of generic_file an object using an incompatible read/write mode"); set_mode(f->get_mode()); to_add.ptr = f; to_add.labels.clear(); if(label != "") to_add.labels.push_back(label); stack.push_back(to_add); } generic_file *pile::pop() { face ret; if(stack.size() > 0) { ret = stack.back(); stack.pop_back(); } else ret.ptr = nullptr; return ret.ptr; // nullptr is returned if the stack is empty } generic_file *pile::get_below(const generic_file *ref) { deque::reverse_iterator it = stack.rbegin(); while(it != stack.rend() && it->ptr != ref) ++it; if(it != stack.rend()) { ++it; // getting the next object, that's it the one below as this is a reverse iterator if(it != stack.rend()) return it->ptr; else return nullptr; } else return nullptr; } generic_file *pile::get_above(const generic_file *ref) { deque::iterator it = stack.begin(); while(it != stack.end() && it->ptr != ref) ++it; if(it != stack.end()) { ++it; // getting the next object, that's it the one above if(it != stack.end()) return it->ptr; else return nullptr; } else return nullptr; } generic_file *pile::get_by_label(const std::string & label) { if(label == "") throw SRC_BUG; else { deque::iterator it = look_for_label(label); if(it == stack.end()) throw Erange("pile::get_by_label", "Label requested in generic_file stack is unknown"); if(it->ptr == nullptr) throw SRC_BUG; return it->ptr; } } void pile::clear_label(const string & label) { if(label == "") throw Erange("pile::clear_label", "Empty string is an invalid label, cannot clear it"); deque::iterator it = look_for_label(label); if(it != stack.end()) { list::iterator lab = find(it->labels.begin(), it->labels.end(), label); if(lab == it->labels.end()) throw SRC_BUG; it->labels.erase(lab); } } void pile::add_label(const string & label) { if(stack.empty()) throw Erange("pile::add_label", "Cannot add a label to an empty stack"); if(label == "") throw Erange("pile::add_label", "An empty string is an invalid label, cannot add it"); if(look_for_label(label) != stack.end()) throw Erange("pile::add_label", "Label already used in stack, cannot add it"); stack.back().labels.push_back(label); } bool pile::skippable(skippability direction, const infinint & amount) { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; return stack.back().ptr->skippable(direction, amount); } else throw Erange("pile::skip", "Error: skippable() on empty stack"); } bool pile::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; return stack.back().ptr->skip(pos); } else throw Erange("pile::skip", "Error: skip() on empty stack"); } bool pile::skip_to_eof() { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; return stack.back().ptr->skip_to_eof(); } else throw Erange("pile::skip_to_eof", "Error: skip_to_eof() on empty stack"); } bool pile::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; return stack.back().ptr->skip_relative(x); } else throw Erange("pile::skip_relative", "Error: skip_relative() on empty stack"); } bool pile::truncatable(const infinint & pos) const { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; return stack.back().ptr->truncatable(pos); } else throw Erange("pile::truncatable", "Error: truncatable() on empty stack"); } infinint pile::get_position() const { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; return stack.back().ptr->get_position(); } else throw Erange("pile::get_position", "Error: get_position() on empty stack"); } void pile::copy_to(generic_file & ref) { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; stack.back().ptr->copy_to(ref); } else throw Erange("pile::copy_to", "Error: copy_to() from empty stack"); } void pile::copy_to(generic_file & ref, const infinint & crc_size, crc * & value) { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; stack.back().ptr->copy_to(ref, crc_size, value); } else throw Erange("pile::copy_to(crc)", "Error: copy_to(crc) from empty stack"); } void pile::inherited_read_ahead(const infinint & amount) { if(is_terminated()) throw SRC_BUG; if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; return stack.back().ptr->read_ahead(amount); } } U_I pile::inherited_read(char *a, U_I size) { if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; return stack.back().ptr->read(a, size); } else throw Erange("pile::skip", "Error: inherited_read() on empty stack"); } void pile::inherited_write(const char *a, U_I size) { if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; stack.back().ptr->write(a, size); } else throw Erange("pile::skip", "Error: inherited_write() on empty stack"); } void pile::inherited_truncate(const infinint & pos) { if(stack.size() > 0) { if(stack.back().ptr == nullptr) throw SRC_BUG; stack.back().ptr->truncate(pos); } else throw Erange("pile::skip", "Error: inherited_write() on empty stack"); } void pile::sync_write_above(generic_file *ptr) { deque::reverse_iterator it = stack.rbegin(); // we start from the top of the stack down to ptr while(it != stack.rend() && it->ptr != ptr) { it->ptr->sync_write(); ++it; } if(it->ptr != ptr) throw SRC_BUG; } void pile::flush_read_above(generic_file *ptr) { deque::reverse_iterator it = stack.rbegin(); // we start from the top of the stack down to ptr while(it != stack.rend() && it->ptr != ptr) { it->ptr->flush_read(); ++it; } if(it->ptr != ptr) throw SRC_BUG; } void pile::inherited_sync_write() { for(deque::reverse_iterator it = stack.rbegin() ; it != stack.rend() ; ++it) if(it->ptr != nullptr) it->ptr->sync_write(); else throw SRC_BUG; } void pile::inherited_flush_read() { for(deque::iterator it = stack.begin() ; it != stack.end() ; ++it) if(it->ptr != nullptr) it->ptr->flush_read(); else throw SRC_BUG; } void pile::inherited_terminate() { for(deque::reverse_iterator it = stack.rbegin() ; it != stack.rend() ; ++it) if(it->ptr != nullptr) it->ptr->terminate(); else throw SRC_BUG; } void pile::detruit() { for(deque::reverse_iterator it = stack.rbegin() ; it != stack.rend() ; ++it) { if(it->ptr != nullptr) { try { delete it->ptr; } catch(...) { // ignore all exceptions } it->ptr = nullptr; } } stack.clear(); } deque::iterator pile::look_for_label(const std::string & label) { deque::iterator it = stack.begin(); while(it != stack.end() && find(it->labels.begin(), it->labels.end(), label) == it->labels.end()) ++it; return it; } } // end of namespace dar-2.7.15/src/libdar/compress_module.hpp0000644000175000017500000000777714636066467015276 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file compress_module.hpp /// \brief provides abstracted interface of per-block compression/decompression /// \ingroup Private #ifndef COMPRESS_MODULE_HPP #define COMPRESS_MODULE_HPP #include "../my_config.h" #include "integers.hpp" #include "compression.hpp" #include namespace libdar { /// \addtogroup Private /// @{ class compress_module { public: compress_module() {}; compress_module(const compress_module & ref) = default; compress_module(compress_module && ref) noexcept = default; compress_module & operator = (const compress_module & ref) = default; compress_module & operator = (compress_module && ref) noexcept = default; virtual ~compress_module() = default; /// return the compression algorithm used by the module virtual compression get_algo() const = 0; /// returns the maximum size of data to be compressed as a single block virtual U_I get_max_compressing_size() const = 0; /// minimal buffer size to compress clear_size of data for compression to be guaranteed to succeed /// \param[in] clear_size is the size of the data buffer to compress /// \return the minimal size of the destination buffer for compression to be garanteed to succeed virtual U_I get_min_size_to_compress(U_I clear_size) const = 0; /// compress a block of data /// \param[in] normal points to the first byte of the data block to compress /// \param[in] normal_size is the number of bytes found at normal address to be compressed /// \param[in] zip_buf is where to put the resulting compressed data /// \param[in] zip_buf_size is the allocated bytes at zip_buf address /// \return the number of bytes effectively used in zip_buf by the compressed data /// \note in case of error a Egeneric based exception should be thrown virtual U_I compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const = 0; /// uncompress a block of data /// \param[in] zip_buf points to the first byte of the compressed data block /// \param[in] zip_buf_size is the number of bytes of the compressed dat starting at zip_buf /// \param[in] normal is the place where to write the uncompressed data /// \param[in] nomral_size is the allocated bytes starting at normal address /// \return the number of bytes written at normal address that constitute the uncompressed data /// \note it is expected that the implementation would throw an libdar::Edata() exception /// on error, like corrupted compressed data submitted for decompression virtual U_I uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const = 0; /// used to duplicate a inherited class of compress_module when it is pointed by a compress_module pointer /// \note this call should provide a pointer to a valid object or throw an exception (Ememory, ...) virtual std::unique_ptr clone() const = 0; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/compress_block_header.cpp0000644000175000017500000000301414636066467016362 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "compress_block_header.hpp" using namespace std; namespace libdar { void compress_block_header::dump(generic_file & f) { f.write(&type, 1); size.dump(f); } bool compress_block_header::set_from(generic_file & f) { bool ret = (f.read(&type, 1) == 1); if(ret) size.read(f); // if read() fails for size while // it succeeded for type, an exception // is thrown calling size.read(f) return ret; } } // end of namespace dar-2.7.15/src/libdar/data_dir.cpp0000644000175000017500000004114114636066467013617 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif } // end extern "C" #include #include "data_dir.hpp" #include "tools.hpp" #include "user_interaction.hpp" #include "deci.hpp" #include "path.hpp" #include "datetime.hpp" #include "cat_all_entrees.hpp" using namespace std; using namespace libdar; namespace libdar { data_dir::data_dir(const string &name) : data_tree(name) { rejetons.clear(); } data_dir::data_dir(generic_file &f, unsigned char db_version) : data_tree(f, db_version) { infinint tmp = infinint(f); // number of children data_tree *entry = nullptr; rejetons.clear(); try { while(!tmp.is_zero()) { entry = read_next_in_list_from_file(f, db_version); if(entry == nullptr) throw Erange("data_dir::data_dir", gettext("Unexpected end of file")); rejetons.push_back(entry); entry = nullptr; --tmp; } } catch(...) { deque::iterator next = rejetons.begin(); while(next != rejetons.end()) { delete *next; *next = nullptr; ++next; } if(entry != nullptr) delete entry; throw; } } data_dir::data_dir(const data_dir & ref) : data_tree(ref) { rejetons.clear(); } data_dir::data_dir(const data_tree & ref) : data_tree(ref) { rejetons.clear(); } data_dir::~data_dir() { deque::iterator next = rejetons.begin(); while(next != rejetons.end()) { delete *next; *next = nullptr; ++next; } } void data_dir::dump(generic_file & f) const { deque::const_iterator it = rejetons.begin(); infinint tmp = rejetons.size(); data_tree::dump(f); tmp.dump(f); while(it != rejetons.end()) { if(*it == nullptr) throw SRC_BUG; (*it)->dump(f); ++it; } } data_tree *data_dir::find_or_addition(const string & name, bool is_dir, const archive_num & archive) { const data_tree *fils = read_child(name); data_tree *ret = nullptr; if(fils == nullptr) // brand-new data_tree to build { if(is_dir) ret = new (nothrow) data_dir(name); else ret = new (nothrow) data_tree(name); if(ret == nullptr) throw Ememory("data_dir::find_or_addition"); add_child(ret); } else // already saved in another archive { // check if dir/file nature has changed const data_dir *fils_dir = dynamic_cast(fils); if(fils_dir == nullptr && is_dir) // need to upgrade data_tree to data_dir { ret = new (nothrow) data_dir(*fils); // upgrade data_tree in an empty data_dir if(ret == nullptr) throw Ememory("data_dir::find_or_addition"); try { remove_child(name); add_child(ret); } catch(...) { delete ret; throw; } } else // no change in dir/file nature ret = const_cast(fils); } return ret; } void data_dir::add(const cat_inode *entry, const archive_num & archive) { const cat_directory *entry_dir = dynamic_cast(entry); const cat_file *entry_file = dynamic_cast(entry); data_tree * tree = find_or_addition(entry->get_name(), entry_dir != nullptr, archive); archive_num last_archive; db_lookup result; datetime last_mod = entry->get_last_modif() > entry->get_last_change() ? entry->get_last_modif() : entry->get_last_change(); const crc *base = nullptr; const crc *res = nullptr; switch(entry->get_saved_status()) { case saved_status::saved: case saved_status::fake: if(entry_file != nullptr) { if(!entry_file->get_crc(res)) res = nullptr; } tree->set_data(archive, last_mod, db_etat::et_saved, base, res); break; case saved_status::delta: if(entry_file == nullptr) throw SRC_BUG; if(!entry_file->get_patch_base_crc(base)) base = nullptr; if(!entry_file->get_patch_result_crc(res)) res = nullptr; tree->set_data(archive, last_mod, db_etat::et_patch, base, res); break; case saved_status::inode_only: if(entry_file != nullptr) { if(!entry_file->get_crc(res)) if(!entry_file->get_patch_result_crc(res)) res = nullptr; if(!entry_file->get_patch_base_crc(base)) base = nullptr; } tree->set_data(archive, last_mod, db_etat::et_inode, base, res); break; case saved_status::not_saved: if(entry_file != nullptr) { if(!entry_file->get_crc(res)) if(!entry_file->get_patch_result_crc(res)) res = nullptr; if(!entry_file->get_patch_base_crc(base)) base = nullptr; } tree->set_data(archive, last_mod, db_etat::et_present, base, res); break; default: throw SRC_BUG; } switch(entry->ea_get_saved_status()) { case ea_saved_status::none: break; case ea_saved_status::removed: result = tree->get_EA(last_archive, datetime(0), false); if(result == db_lookup::found_present || result == db_lookup::not_restorable) tree->set_EA(archive, entry->get_last_change(), db_etat::et_removed); // else no need to add an db_etat::et_remove entry in the map break; case ea_saved_status::partial: tree->set_EA(archive, entry->get_last_change(), db_etat::et_present); break; case ea_saved_status::fake: case ea_saved_status::full: tree->set_EA(archive, entry->get_last_change(), db_etat::et_saved); break; default: throw SRC_BUG; } } void data_dir::add(const cat_detruit *entry, const archive_num & archive) { data_tree * tree = find_or_addition(entry->get_name(), false, archive); set last_archive_set; archive_num last_archive; db_lookup result; result = tree->get_data(last_archive_set, datetime(0), false); if(result == db_lookup::found_present || result == db_lookup::not_restorable) tree->set_data(archive, entry->get_date(), db_etat::et_removed); result = tree->get_EA(last_archive, datetime(0), false); if(result == db_lookup::found_present || result == db_lookup::not_restorable) tree->set_EA(archive, entry->get_date(), db_etat::et_removed); } const data_tree *data_dir::read_child(const string & name) const { deque::const_iterator it = rejetons.begin(); while(it != rejetons.end() && *it != nullptr && (*it)->get_name() != name) ++it; if(it == rejetons.end()) return nullptr; else if(*it == nullptr) throw SRC_BUG; else return *it; } void data_dir::read_all_children(vector & fils) const { deque::const_iterator it = rejetons.begin(); fils.clear(); while(it != rejetons.end()) fils.push_back((*it++)->get_name()); } bool data_dir::check_order(user_interaction & dialog, const path & current_path, bool & initial_warn) const { deque::const_iterator it = rejetons.begin(); bool ret = data_tree::check_order(dialog, current_path, initial_warn); path subpath = current_path.display() == "." ? get_name() : current_path.append(get_name()); while(it != rejetons.end() && ret) { if(*it == nullptr) throw SRC_BUG; ret = (*it)->check_order(dialog, subpath, initial_warn); ++it; } return ret; } void data_dir::finalize(const archive_num & archive, const datetime & deleted_date, const archive_num & ignore_archives_greater_or_equal) { datetime new_deleted_date; set tmp_archive_set; db_etat tmp_presence; data_tree::finalize(archive, deleted_date, ignore_archives_greater_or_equal); switch(get_data(tmp_archive_set, datetime(0), false)) { case db_lookup::found_present: case db_lookup::found_removed: break; // acceptable result case db_lookup::not_found: if(fix_corruption()) throw Edata("This is to signal the caller of this method that this object has to be removed from database"); // exception caugth in data_dir::finalize_except_self throw Erange("data_dir::finalize", gettext("This database has been corrupted probably due to a bug in release 2.4.0 to 2.4.9, and it has not been possible to cleanup this corruption, please rebuild the database from archives or extracted \"catalogues\", if the database has never been used by one of the previously mentioned released, you are welcome to open a bug report and provide as much as possible details about the circumstances")); case db_lookup::not_restorable: break; // also an acceptable result; default: throw SRC_BUG; } if(tmp_archive_set.empty()) throw SRC_BUG; if(!read_data(*(tmp_archive_set.rbegin()), new_deleted_date, tmp_presence)) throw SRC_BUG; finalize_except_self(archive, new_deleted_date, ignore_archives_greater_or_equal); } void data_dir::finalize_except_self(const archive_num & archive, const datetime & deleted_date, const archive_num & ignore_archives_greater_or_equal) { deque::iterator it = rejetons.begin(); while(it != rejetons.end()) { if(*it == nullptr) throw SRC_BUG; try { (*it)->finalize(archive, deleted_date, ignore_archives_greater_or_equal); ++it; } catch(Edata & e) { delete (*it); rejetons.erase(it); it = rejetons.begin(); } } } bool data_dir::remove_all_from(const archive_num & archive_to_remove, const archive_num & last_archive) { deque::iterator it = rejetons.begin(); while(it != rejetons.end()) { if((*it) == nullptr) throw SRC_BUG; if((*it)->remove_all_from(archive_to_remove, last_archive)) { delete *it; // release the memory used by the object *it = nullptr; rejetons.erase(it); // remove the entry from the deque it = rejetons.begin(); // does not seems "it" points to the next item after erase, so we restart from the beginning } else ++it; } return data_tree::remove_all_from(archive_to_remove, last_archive) && rejetons.size() == 0; } void data_dir::show(database_listing_show_files_callback callback, void *tag, archive_num num, string marge) const { deque::const_iterator it = rejetons.begin(); set ou_data; archive_num ou_ea; bool data, ea; string name; db_lookup lo_data, lo_ea; bool even_when_removed = (num != 0); while(it != rejetons.end()) { if(*it == nullptr) throw SRC_BUG; data_dir *dir = dynamic_cast(*it); lo_data = (*it)->get_data(ou_data, datetime(0), even_when_removed); lo_ea = (*it)->get_EA(ou_ea, datetime(0), even_when_removed); data = lo_data == db_lookup::found_present && (ou_data.find(num) != ou_data.end() || num == 0); ea = lo_ea == db_lookup::found_present && (ou_ea == num || num == 0); name = marge + (*it)->get_name(); if(data || ea || num == 0) { if(callback == nullptr) throw Erange("data_dir::show", "nullptr provided as user callback function"); try { callback(tag, name, data, ea); } catch(...) { throw Elibcall("data_dir::show", "user provided callback function should not throw any exception"); } } if(dir != nullptr) dir->show(callback, tag, num, name + "/"); ++it; } } void data_dir::apply_permutation(archive_num src, archive_num dst) { deque::iterator it = rejetons.begin(); data_tree::apply_permutation(src, dst); while(it != rejetons.end()) { (*it)->apply_permutation(src, dst); ++it; } } void data_dir::skip_out(archive_num num) { deque::iterator it = rejetons.begin(); data_tree::skip_out(num); while(it != rejetons.end()) { (*it)->skip_out(num); ++it; } } void data_dir::compute_most_recent_stats(deque & data, deque & ea, deque & total_data, deque & total_ea) const { deque::const_iterator it = rejetons.begin(); data_tree::compute_most_recent_stats(data, ea, total_data, total_ea); while(it != rejetons.end()) { (*it)->compute_most_recent_stats(data, ea, total_data, total_ea); ++it; } } bool data_dir::fix_corruption() { while(rejetons.begin() != rejetons.end() && *(rejetons.begin()) != nullptr && (*(rejetons.begin()))->fix_corruption()) { delete *(rejetons.begin()); rejetons.erase(rejetons.begin()); } if(rejetons.begin() != rejetons.end()) return false; else return data_tree::fix_corruption(); } bool data_dir::data_tree_find(path chemin, const data_tree *& ptr) const { string filename; const data_dir *current = this; bool loop = true; if(!chemin.is_relative()) throw SRC_BUG; while(loop) { if(!chemin.pop_front(filename)) { filename = chemin.display(); loop = false; } ptr = current->read_child(filename); if(ptr == nullptr) loop = false; if(loop) { current = dynamic_cast(ptr); if(current == nullptr) { loop = false; ptr = nullptr; } } } return ptr != nullptr; } void data_dir::data_tree_update_with(const cat_directory *dir, archive_num archive) { const cat_nomme *entry; if(dir == nullptr) throw SRC_BUG; dir->reset_read_children(); while(dir->read_children(entry)) { const cat_directory *entry_dir = dynamic_cast(entry); const cat_inode *entry_ino = dynamic_cast(entry); const cat_mirage *entry_mir = dynamic_cast(entry); const cat_detruit *entry_det = dynamic_cast(entry); if(entry_mir != nullptr) { entry_ino = entry_mir->get_inode(); entry_mir->get_inode()->change_name(entry_mir->get_name()); } if(entry_ino == nullptr) if(entry_det != nullptr) { if(!entry_det->get_date().is_null()) add(entry_det, archive); // else this is an old archive that does not store date with cat_detruit objects } else continue; // continue with next loop, we ignore entree objects that are neither inode nor cat_detruit else add(entry_ino, archive); if(entry_dir != nullptr) // going into recursion { data_tree *new_root = const_cast(read_child(entry->get_name())); data_dir *new_root_dir = dynamic_cast(new_root); if(new_root == nullptr) throw SRC_BUG; // the add() method did not add an item for "entry" if(new_root_dir == nullptr) throw SRC_BUG; // the add() method did not add a data_dir item new_root_dir->data_tree_update_with(entry_dir, archive); } } } data_dir *data_dir::data_tree_read(generic_file & f, unsigned char db_version) { data_tree *lu = read_next_in_list_from_file(f, db_version); data_dir *ret = dynamic_cast(lu); if(ret == nullptr && lu != nullptr) delete lu; return ret; } void data_dir::add_child(data_tree *fils) { if(fils == nullptr) throw SRC_BUG; rejetons.push_back(fils); } void data_dir::remove_child(const string & name) { deque::iterator it = rejetons.begin(); while(it != rejetons.end() && *it != nullptr && (*it)->get_name() != name) ++it; if(it != rejetons.end()) { if(*it == nullptr) throw SRC_BUG; else rejetons.erase(it); } } data_tree *data_dir::read_next_in_list_from_file(generic_file & f, unsigned char db_version) { char sign; data_tree *ret; if(f.read(&sign, 1) != 1) return nullptr; // nothing more to read if(sign == data_tree::signature()) ret = new (nothrow) data_tree(f, db_version); else if(sign == data_dir::signature()) ret = new (nothrow) data_dir(f, db_version); else throw Erange("read_next_in_list_from_file", gettext("Unknown record type")); if(ret == nullptr) throw Ememory("read_next_in_list_from_file"); return ret; } } // end of namesapce dar-2.7.15/src/libdar/libdar.hpp0000644000175000017500000001210414636067146013302 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ // NOTE : The following comments are used by doxygen to generate the documentation of reference /// \mainpage /// You will find here the reference documentation for the dar and libdar source code, split in several "modules". /// - API module: contains all information for using libdar within your program /// - Private module: contains all libdar internal documentation, you do not have to read it in order use libdar /// - CMDLINE module: contains the documentation for command-line tools, /// you might want to have a look for illustration of libdar usage. /// - API5 module: contains API v5 symbols as found on release 2.4.x and 2.5.x /// . /// /// \defgroup API API /// \brief APlication Interface /// /// This namespace gathers all symbols that may be accessed from an external /// program. Other symbols are not as much documented, and /// may change or be removed without any warning or backward /// compatibility support. So only use the function, macro, types, /// classes... defined as member of the API module in you external programs. /// /// Please not that an API tutorial is also available and should be a starting point for using libdar, /// nevertheless, if you want to start from here, there is four main classes consider and several /// datastructures aside that they depend on: /// - class libdar::archive you can create and manipulate dar archive like the dar command-line tool does /// - class libdar::database you can create and manupulate dar_manager database, like the dar_manager CLI tool does /// - class libdar::libdar_xform you have guessed, this is the API for dar_xform /// - class libdar::libdar_slave still logicial, this is the API for dar_slave /// all the CLI command mentionned above do rely on these classes so it might help having a look at the /// CMDLINE namespace for illustration on how to use this classes. /// \defgroup Private Private /// \brief Libdar internal symbols /// /// Understanding this is not necessary to use libdar through the API. /// This is libdar internal code documentation /// \defgroup API5 API5 /// \brief APplication Interface backward compatibility for API version 5 /// /// backward compatible namespace with dar/libdar releases 2.5.x and 2.4.x /// \file libdar.hpp /// \brief the main file of the libdar API definitions /// \ingroup API #ifndef LIBDAR_HPP #define LIBDAR_HPP #include "../my_config.h" // the mandatory libdar initialization routine #include "get_version.hpp" // archive class abstraction, this is a good starting point to create and read dar archives #include "archive.hpp" // dar_manager API #include "database.hpp" // dar_xform API #include "libdar_xform.hpp" // dar_slave API #include "libdar_slave.hpp" // common set of exception used within libdar #include "erreurs.hpp" // if you want to know which feature has been activated at compilation time #include "compile_time_features.hpp" // for remote reposity you will need to create such object and pass it where needed #include "entrepot_libcurl.hpp" // for local filesystem, you should not need to create such object to call libdar as it is the default repo used #include "entrepot_local.hpp" // the options class to give non default parameter to the archive class #include "archive_options_listing_shell.hpp" // if you want to bind user input/output to shell command line #include "shell_interaction.hpp" // if you want to bind user intput/output to your own provided callback functions #include "user_interaction_callback.hpp" // this is a trivial way to ignore user input/output #include "user_interaction_blind.hpp" // to redirect to a shell_interaction user I/O object any type of user interaction #include "shell_interaction_emulator.hpp" // to be able to properly cancel a running libdar thread #include "thread_cancellation.hpp" // for even more flexibility you can create your own class inherited from class user_interaction // (see user_interaction.hpp include file) #endif dar-2.7.15/src/libdar/contextual.cpp0000644000175000017500000000231214636066467014233 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end of extern "C" #include "contextual.hpp" using namespace std; namespace libdar { // all method of contextual are inlined or pure virtual ones } // end of namespace dar-2.7.15/src/libdar/path.cpp0000644000175000017500000002100214636255166012772 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif } #include #include "path.hpp" #include "tools.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { const std::string PSEUDO_ROOT = ""; const path FAKE_ROOT(PSEUDO_ROOT, true); /// extract the first path member of a given path /// \param[in,out] p is the given path (in), it receive the new path without the first path member /// \param[out] root this argument receive the first path member extracted from argument 'p' /// \return false if given path is empty, true else. May throw exception in case of invalid path given static bool path_get_root(string & p, string & root); path::path(const string & chem, bool x_undisclosed) { NLS_SWAP_IN; try { string tmp; string s; undisclosed = x_undisclosed; try { dirs.clear(); if(chem.empty()) throw Erange("path::path", gettext("Empty string is not a valid path")); if(chem == "/") undisclosed = false; relative = (chem[0] != '/'); if(!relative) s = string(chem.begin()+1, chem.end()); // remove the leading '/' else s = chem; if(undisclosed) // if last char is '/' need to remove it { string::iterator last = tools_find_last_char_of(s, '/'); if(last + 1 == s.end()) // this is the last char of s s = string(s.begin(), last); } if(undisclosed) dirs.push_back(s); else while(path_get_root(s, tmp)) dirs.push_back(tmp); if(dirs.empty() && relative) throw Erange("path::path", gettext("Empty string is not a valid path")); if(!undisclosed) reduce(); reading = dirs.begin(); } catch(Erange & e) { string e_tmp = e.get_message(); throw Erange("path::path", tools_printf(gettext("%S is an not a valid path: %S"), &chem, &e_tmp)); } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } path::path(const path & ref) { dirs = ref.dirs; relative = ref.relative; undisclosed = ref.undisclosed; reading = dirs.begin(); } path & path::operator = (const path & ref) { dirs = ref.dirs; relative = ref.relative; undisclosed = ref.undisclosed; reading = dirs.begin(); return *this; } bool path::operator == (const path & ref) const { string me = display(); string you = ref.display(); return me == you; } string path::basename() const { if(! dirs.empty()) return dirs.back(); else return "/"; } bool path::read_subdir(string & r) const { if(reading != dirs.end()) { r = *reading; ++reading; return true; } else return false; } bool path::pop(string &arg) { if(relative) if(dirs.size() > 1) { arg = dirs.back(); dirs.pop_back(); return true; } else return false; else if(!dirs.empty()) { arg = dirs.back(); dirs.pop_back(); return true; } else return false; } bool path::pop_front(string & arg) { if(relative) if(dirs.size() > 1) { arg = dirs.front(); dirs.pop_front(); return true; } else return false; else if(!dirs.empty()) { relative = true; arg = "/"; return true; } else return false; } path & path::operator += (const path &arg) { if(!arg.relative) throw Erange("path::operator +", dar_gettext("Cannot add an absolute path")); list::const_iterator it = arg.dirs.begin(); list::const_iterator it_fin = arg.dirs.end(); while(it != it_fin) { if(*it != string(".")) dirs.push_back(*it); ++it; } if(arg.undisclosed) undisclosed = true; reduce(); return *this; } path & path::operator += (const std::string & sub) { dirs.push_back(sub); reduce(); return *this; } bool path::is_subdir_of(const path & p, bool case_sensit) const { string me; string you; if(!case_sensit) { // converting all string in upper case tools_to_upper(display(), me); tools_to_upper(p.display(), you); } else { me = display(); you = p.display(); } if(me.size() >= you.size()) if(strncmp(me.c_str(), you.c_str(), you.size()) == 0) if(me.size() > you.size()) return (you.size() > 1 && me[you.size()] == '/') || (you.size() == 1 && you[0] == '/'); else // thus, me.size() == you.size(), thus I'm a subdir of myself return true; else // path differs in the common length part, cannot be a subdir of "you" return false; else // I am shorter in length, cannot be a subdir of "you" return false; } string path::display() const { string ret = relative ? "" : "/"; list::const_iterator it = dirs.begin(); if(it != dirs.end()) ret += *it++; while(it != dirs.end()) ret = ret + "/" + *it++; return ret; } string path::display_without_root() const { string ret = ""; list::const_iterator it = dirs.begin(); if(relative) ++it; // else we as we ignore the leading / we are all set if(it != dirs.end()) ret += *it++; while(it != dirs.end()) ret += string("/") + *it++; return ret; } void path::explode_undisclosed() const { path *me = const_cast(this); if(!undisclosed) return; try { string res = display(); path tmp = path(res, false); *me = tmp; } catch(...) { reading = dirs.begin(); } } void path::reduce() { dirs.remove("."); if(relative && dirs.empty()) dirs.push_back("."); else { list::iterator it = dirs.begin(); list::iterator prev = it; while(it != dirs.end()) { if(*it == ".." && *prev != "..") { list::iterator tmp = prev; it = dirs.erase(it); if(prev != dirs.begin()) { --prev; dirs.erase(tmp); } else { dirs.erase(prev); prev = dirs.begin(); } } else { prev = it; ++it; } } if(relative && dirs.empty()) dirs.push_back("."); } } static bool path_get_root(string & p, string & root) { string::iterator it = p.begin(); if(p.empty()) return false; while(it != p.end() && *it != '/' ) ++it; root = string(p.begin(), it); if(it != p.end()) p = string(it+1, p.end()); else p = ""; if(root.empty()) throw Erange("path_get_root", dar_gettext("Empty string as subdirectory does not make a valid path")); return true; } } // end of namespace dar-2.7.15/src/libdar/libdar_xform.hpp0000644000175000017500000001425414636066467014532 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file libdar_xform.hpp /// \brief API for dar_xform functionnality /// \ingroup API #ifndef LIBDAR_XFORM_HPP #define LIBDAR_XFORM_HPP #include "../my_config.h" #include "infinint.hpp" #include "user_interaction.hpp" #include "archive_aux.hpp" #include namespace libdar { /// \addtogroup API /// @{ /// class implementing the dar_xform feature class libdar_xform { public: /// the archive to transform is read from a set of slices /// \param[in] ui for user interaction, may be set to std::nullptr /// \param[in] chem the path where resides the archive /// \param[in] basename the basename of the archive /// \param[in] extension should be set to "dar" as always /// \param[in] min_digits the way slice number is written in files, /// use 0 if this feature was not used at creation time /// \param[in] execute command to execute before each new slice /// same substitution is available as archive_options_create::set_execute() libdar_xform(const std::shared_ptr & ui, const std::string & chem, const std::string & basename, const std::string & extension, const infinint & min_digits, const std::string & execute); /// the archive to transform is read from a named pipe /// \param[in] dialog for user interaction, may be set to std::nullptr /// \param[in] pipename named pipe where to read the archive from (single sliced one) libdar_xform(const std::shared_ptr & dialog, const std::string & pipename); /// < if pipename is set to "-" reading from standard input /// the archive to transform is read from a file descriptor open in read mode /// \param[in] dialog for user interaction, may be set to std::nullptr /// \param[in] filedescriptor the filedescriptor to reading the archive from libdar_xform(const std::shared_ptr & dialog, int filedescriptor); /// copy constructor is not allowed libdar_xform(const libdar_xform & ref) = delete; /// move constructor libdar_xform(libdar_xform && ref) noexcept; /// assignment operator is not allowed libdar_xform & operator = (const libdar_xform & ref) = delete; /// move assignment operator libdar_xform & operator = (libdar_xform && ref) noexcept; /// destructor ~libdar_xform(); /// the resulting archive is a written to disk possibly multi-sliced /// \param[in] path directory where to write the new archive to /// \param[in] basename archive base name to create /// \param[in] extension should be set to "dar" as always /// \param[in] allow_over whether to allow slice overwriting /// \param[in] warn_over whether to warn before overwriting a slice /// \param[in] pause the number of slice to pause asking the user for continuation. Set /// to zero to disable pausing /// \param[in] first_slice_size size of the first slice if different of the other. Set /// to zero to have the first slice having the same size as others /// \param[in] slice_size size of slices (except the first slice which may be different). /// set to zero if slicing is not wanted /// \param[in] slice_perm number written in octal corresponding to the permission of slices /// to create. if set to an empty string the slice permission will not be overriden and will /// follow the umask() value /// \param[in] slice_user user name or UID to assign slice to. Assuming the process has /// the permission/capabilities to change UID of files /// \param[in] slice_group group name or GID to assign slice to. Assuming the process has /// the permission/capabilities to change GID of files /// \param[in] hash hashing algorithm to rely on generate hash file for each slice. Use /// libdar::hash_algo::hash_none to disable this feature /// \param[in] min_digits numbering of slices in filename should be padded by as much zero /// to have no less than this number of digit. Use zero to disable this feature /// \param[in] execute command to execute after each slice has been completed. Same /// substitution is available as archive_options_create::set_execute void xform_to(const std::string & path, const std::string & basename, const std::string & extension, bool allow_over, bool warn_over, const infinint & pause, const infinint & first_slice_size, const infinint & slice_size, const std::string & slice_perm, const std::string & slice_user, const std::string & slice_group, hash_algo hash, const infinint & min_digits, const std::string & execute); /// the resulting archive is a single sliced archive sent to a filedescriptor /// \param[in] filedescriptor file descriptor open in write mode to write the archive to /// \param[in] execute command to execute after the archive has been completed. /// same string substitution available as described in other xform_to method void xform_to(int filedescriptor, const std::string & execute); private: class i_libdar_xform; std::unique_ptr pimpl; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/defile.hpp0000644000175000017500000000510314636066467013303 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file defile.hpp /// \brief here is defined the defile class /// \ingroup Private #ifndef DEFILE_HPP #define DEFILE_HPP #include "../my_config.h" #include "path.hpp" #include "cat_entree.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the defile class keep trace of the real path of files while the flow in the filter routines /// the filter routines manipulates flow of inode, where their relative order /// represent the directory structure. To be able to know what is the real path /// of the current inode, all previously passed inode must be known. /// this class is used to display the progression of the filtering routing, /// and the file on which the filtering routine operates class defile { public : defile(const path &racine) : chemin(racine) { init = true; cache_set = none; }; defile(const defile & ref) = default; defile(defile && ref) noexcept = default; defile & operator = (const defile & ref) = default; defile & operator = (defile && ref) noexcept = default; ~defile() = default; void enfile(const cat_entree *e); const path & get_path() const { return chemin; }; const std::string & get_string() const; const std::string & get_string_without_root() const; private : path chemin; ///< current path bool init; ///< true if reached the "root" (all pushed arguments have been poped) mutable enum { none, full, without_root } cache_set; ///< whether cache is accurate mutable std::string cache; ///< cache of "chemin" converted into string }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive_num.cpp0000644000175000017500000000343214636066467014351 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif } // end extern "C" #include "archive_num.hpp" #include "generic_file.hpp" using namespace std; namespace libdar { void archive_num::read_from_file(generic_file &f) { char buffer[val_size]; U_16 *ptr = (U_16 *)&(buffer[0]); f.read(buffer, val_size); val = ntohs(*ptr); } void archive_num::write_to_file(generic_file &f) const { char buffer[val_size]; U_16 *ptr = (U_16 *)&(buffer[0]); *ptr = htons(val); f.write(buffer, val_size); } } // end of namespace dar-2.7.15/src/libdar/user_interaction_callback5.hpp0000644000175000017500000002174414636067146017335 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file user_interaction_callback5.hpp /// \brief API v5 backward compatible user_interaction_callback class /// \ingroup API5 #ifndef USER_INTERACTION_CALLBACK5_HPP #define USER_INTERACTION_CALLBACK5_HPP #include "../my_config.h" #include "user_interaction5.hpp" namespace libdar5 { /// \addtogroup API5 /// @{ /// full implemented class for user_interaction based on callback functions. /// this class is an inherited class of user_interaction it is used by /// dar command line programs, but you can use it if you wish. /// \ingroup API5 class user_interaction_callback : public user_interaction { public: /// constructor which receive the callback functions. /// \param[in] x_warning_callback is used by warning() method /// \param[in] x_answer_callback is used by the pause() method /// \param[in] x_string_callback is used by get_string() method /// \param[in] x_secu_string_callback is used by get_secu_string() method /// \param[in] context_value will be passed as last argument of callbacks when /// called from this object. /// \note The context argument of each callback is set with the context_value given /// in the user_interaction_callback object constructor. The value can /// can be any arbitrary value (nullptr is valid), and can be used as you wish. /// Note that the listing callback is not defined here, but thanks to a specific method user_interaction_callback(void (*x_warning_callback)(const std::string &x, void *context), bool (*x_answer_callback)(const std::string &x, void *context), std::string (*x_string_callback)(const std::string &x, bool echo, void *context), secu_string (*x_secu_string_callback)(const std::string &x, bool echo, void *context), void *context_value); user_interaction_callback(const user_interaction_callback & ref) = default; user_interaction_callback(user_interaction_callback && ref) noexcept = default; user_interaction_callback & operator = (const user_interaction_callback & ref) = default; user_interaction_callback & operator = (user_interaction_callback && ref) noexcept = default; ~user_interaction_callback() = default; /// overwritting method from parent class. virtual void pause(const std::string & message) override; /// overwritting method from parent class. virtual std::string get_string(const std::string & message, bool echo) override; /// overwritting method from parent class. virtual secu_string get_secu_string(const std::string & message, bool echo) override; /// overwritting method from parent class. virtual void listing(const std::string & flag, const std::string & perm, const std::string & uid, const std::string & gid, const std::string & size, const std::string & date, const std::string & filename, bool is_dir, bool has_children) override; /// overwritting method from parent class virtual void dar_manager_show_files(const std::string & filename, bool available_data, bool available_ea) override; /// overwritting method from parent class virtual void dar_manager_contents(U_I number, const std::string & chemin, const std::string & archive_name) override; /// overwritting method from parent class virtual void dar_manager_statistics(U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea) override; /// overwritting method from parent class virtual void dar_manager_show_version(U_I number, const std::string & data_date, const std::string & data_presence, const std::string & ea_date, const std::string & ea_presence) override; /// You can set a listing callback thanks to this method. /// If set, when file listing will this callback function will /// be used instead of the x_warning_callback given as argument /// of the constructor. void set_listing_callback(void (*callback)(const std::string & flag, const std::string & perm, const std::string & uid, const std::string & gid, const std::string & size, const std::string & date, const std::string & filename, bool is_dir, bool has_children, void *context)) { tar_listing_callback = callback; set_use_listing(true); // this is to inform libdar to use listing() }; /// You can set a dar_manager_show_files callback thanks to this method void set_dar_manager_show_files_callback(void (*callback)(const std::string & filename, bool available_data, bool available_ea, void *context)) { dar_manager_show_files_callback = callback; set_use_dar_manager_show_files(true); // this is to inform libdar to use the dar_manager_show_files() method }; void set_dar_manager_contents_callback(void (*callback)(U_I number, const std::string & chemin, const std::string & archive_name, void *context)) { dar_manager_contents_callback = callback; set_use_dar_manager_contents(true); // this is to inform libdar to use the dar_manager_contents() method }; void set_dar_manager_statistics_callback(void (*callback)(U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea, void *context)) { dar_manager_statistics_callback = callback; set_use_dar_manager_statistics(true); // this is to inform libdar to use the dar_manager_statistics() method }; void set_dar_manager_show_version_callback(void (*callback)(U_I number, const std::string & data_date, const std::string & data_presence, const std::string & ea_date, const std::string & ea_presence, void *context)) { dar_manager_show_version_callback = callback; set_use_dar_manager_show_version(true); // this is to inform libdar to use the dar_manager_show_version() method }; /// overwritting method from parent class. virtual user_interaction *clone() const override; protected: /// change the context value of the object that will be given to callback functions void change_context_value(void *new_value) { context_val = new_value; }; /// overwritting method from parent class. virtual void inherited_warning(const std::string & message) override; private: void (*warning_callback)(const std::string & x, void *context); // pointer to function bool (*answer_callback)(const std::string & x, void *context); // pointer to function std::string (*string_callback)(const std::string & x, bool echo, void *context); // pointer to function secu_string (*secu_string_callback)(const std::string & x, bool echo, void *context); // pointer to function void (*tar_listing_callback)(const std::string & flags, const std::string & perm, const std::string & uid, const std::string & gid, const std::string & size, const std::string & date, const std::string & filename, bool is_dir, bool has_children, void *context); void (*dar_manager_show_files_callback)(const std::string & filename, bool available_data, bool available_ea, void *context); void (*dar_manager_contents_callback)(U_I number, const std::string & chemin, const std::string & archive_name, void *context); void (*dar_manager_statistics_callback)(U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea, void *context); void (*dar_manager_show_version_callback)(U_I number, const std::string & data_date, const std::string & data_presence, const std::string & ea_date, const std::string & ea_presence, void *context); void *context_val; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/pile_descriptor.hpp0000644000175000017500000000366214636066467015252 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file pile_descriptor.hpp /// \brief optimization structure to quickly access some commonly used layers of a stack of generic_file /// \ingroup Private #ifndef PILE_DESCRIPTOR_HPP #define PILE_DESCRIPTOR_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "pile.hpp" #include "escape.hpp" #include "proto_compressor.hpp" namespace libdar { /// \addtogroup Private /// @{ struct pile_descriptor { pile *stack; ///< the stack to read from or write to (should never be equal to nullptr) escape *esc; ///< an escape layer in stack (may be nullptr) proto_compressor *compr; ///< a compressor layer in stack (should never be equal to nullptr) pile_descriptor() { stack = nullptr; esc = nullptr; compr = nullptr; }; pile_descriptor(pile *ptr); void check(bool small) const; ///< check structure coherence with expected read/write mode (small or normal) }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/entree_stats.hpp0000644000175000017500000000627714636066467014570 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file entree_stats.hpp /// \brief datastructure holding the nature of file present in a given archive /// \ingroup API #ifndef ENTREE_STATS_HPP #define ENTREE_STATS_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "infinint.hpp" #include "user_interaction.hpp" #include namespace libdar { class cat_entree; /// \addtogroup API /// @{ /// holds the statistics contents of a catalogue struct entree_stats { infinint num_x; ///< number of file referenced as destroyed since last backup infinint num_d; ///< number of directories infinint num_f; ///< number of plain files (hard link or not, thus file directory entries) infinint num_c; ///< number of char devices infinint num_b; ///< number of block devices infinint num_p; ///< number of named pipes infinint num_s; ///< number of unix sockets infinint num_l; ///< number of symbolic links infinint num_D; ///< number of Door infinint num_hard_linked_inodes; ///< number of inode that have more than one link (inode with "hard links") infinint num_hard_link_entries; ///< total number of hard links (file directory entry pointing to an ///< inode already linked in the same or another directory (i.e. hard linked)) infinint saved; ///< total number of saved inode (unix inode, not inode class) hard links do not count here infinint patched; ///< total number of saved data as binary delta patch infinint inode_only; ///< total number of inode which metadata changed without data being modified infinint total; ///< total number of inode in archive (unix inode, not inode class) hard links do not count here void clear() { num_x = num_d = num_f = num_c = num_b = num_p = num_s = num_l = num_D = num_hard_linked_inodes = num_hard_link_entries = saved = patched = inode_only = total = 0; }; void add(const cat_entree *ref); void listing(user_interaction & dialog) const; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/tlv_list.hpp0000644000175000017500000000422114636066467013713 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file tlv_list.hpp /// \brief List of Generic Type Length Value data structures /// \ingroup Private #ifndef TLV_LIST_HPP #define TLV_LIST_HPP #include "tlv.hpp" #include "generic_file.hpp" #include namespace libdar { /// \addtogroup Private /// @{ class tlv_list { public: /// builds an empty list tlv_list() {}; // field "contents" is an object initialized by its default constructor tlv_list(generic_file & f) { init(f); }; ///< builds a list from a file tlv_list(const tlv_list & ref) = default; tlv_list(tlv_list && ref) = default; tlv_list & operator = (const tlv_list & ref) = default; tlv_list & operator = (tlv_list && ref) noexcept = default; ~tlv_list() = default; void dump(generic_file & f) const; ///< dump tlv_list to file void read(generic_file & f) { init(f); }; ///< erase and read a list from a file U_I size() const { return contents.size(); }; tlv & operator[] (U_I item) const; void clear() { contents.clear(); }; void add(const tlv & next) { contents.push_back(next); }; private: std::deque contents; void init(generic_file & f); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cache.cpp0000644000175000017500000004754414636067146013123 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_LIMITS_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif } #include "cache.hpp" using namespace std; namespace libdar { cache::cache(generic_file & hidden, bool shift_mode, U_I x_size) : generic_file(hidden.get_mode()) // except if hidden is read-only we provide read-write facility in // the cache, for what is out of the cache we check // the underlying object mode { // sanity checks if(x_size < 10) throw Erange("cache::cache", gettext("wrong value given as initial_size argument while initializing cache")); ref = & hidden; buffer = nullptr; alloc_buffer(x_size); next = 0; last = 0; first_to_write = size; buffer_offset = ref->get_position(); shifted_mode = shift_mode; } cache::~cache() { try { flush_write(); } catch(...) { // ignore all exceptions } release_buffer(); } bool cache::skippable(skippability direction, const infinint & amount) { infinint in_cache = available_in_cache(direction); // either available data is enough to assure skippability or we // calculate the direction and amount to ask to the lower layer (ref) if(in_cache >= amount) { // skippability is contained in the cached data switch(direction) { case skip_forward: return true; case skip_backward: // sanity check if(infinint(next) < amount) throw SRC_BUG; if(first_to_write != size) { // some data is pending for writing // the cached data before first_to_write has already // been written to the cached layer, if we have to // skip before this position, we have to check the // cached object will allow skipping backward to // overwrite the already written data infinint new_ftw = infinint(next) - amount; if(infinint(first_to_write) <= new_ftw) // we don't lead the cached object to skip backward // in order to overwrite data return true; else // we must check whether the cached object will allow skipping backward { infinint backw = infinint(first_to_write) - new_ftw; return ref->skippable(skip_backward, backw); } } else // no write pending data return true; default: throw SRC_BUG; } return true; } else { switch(direction) { case skip_forward: if(ref->get_position() <= buffer_offset) return ref->skippable(direction, buffer_offset - ref->get_position() + next + amount); else { infinint backw = ref->get_position() - buffer_offset; infinint forw = amount + next; if(backw >= forw) return ref->skippable(skip_backward, backw - forw); else return ref->skippable(skip_forward, forw - backw); } case skip_backward: // if we don't flush write pending data, the cached object can evaluate backward // skippable possible while it could not be possible once the data pending for // writing will be flushed (sar object for example) // But flushing data now, does not remove it all from the cache and some may remain // which could later be modified from the cache only. Then once time will come to // flush this data, this would mean for the cached object to skip backward to modify // the already flushed data. Skipping backward may be impossible at all (tuyau) so the // data flushing would fail and the global write operation on the cache object // would also // so we must flush write pending data // but control the first_to_write field skippability is contained insde the cache // for that when it decreases a check is done for backward skippability on the cached object if(need_flush_write()) flush_write(); // before skipping we would first have to write the pending data so we do it // now for the underlay provides a coherent answer from skippable now with the effective skip at later time if(ref->get_position() >= buffer_offset) { infinint backw = ref->get_position() - buffer_offset + amount; infinint forw = next; if(backw >= forw) return ref->skippable(skip_backward, backw - forw); else return ref->skippable(skip_forward, forw - backw); } else { infinint backw = amount; infinint forw = buffer_offset - ref->get_position() + next; if(backw >= forw) return ref->skippable(skip_backward, backw - forw); else return ref->skippable(skip_forward, forw - backw); } default: throw SRC_BUG; } } } bool cache::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; if(pos >= buffer_offset && pos <= buffer_offset + last) { // skipping inside the buffer is possible infinint tmp_next = pos - buffer_offset; U_I new_next = 0; tmp_next.unstack(new_next); if(!tmp_next.is_zero()) throw SRC_BUG; // assigning to next its new value to reflect the skip() operation if(first_to_write > new_next && first_to_write != size) { // reducing first_to_write means the cached object // will have to skip backward, this we must check if(ref->skippable(skip_backward, first_to_write - new_next)) first_to_write = new_next; else return false; // cannot skip } next = new_next; return true; } else // skipping would lead the current position to be outside the buffer { bool ret; if(need_flush_write()) flush_write(); next = last = 0; ret = ref->skip(pos); buffer_offset = ref->get_position(); return ret; } } bool cache::skip_to_eof() { bool ret; infinint max; if(is_terminated()) throw SRC_BUG; if(need_flush_write()) flush_write(); if(eof_offset.is_zero()) { ret = ref->skip_to_eof(); eof_offset = ref->get_position(); } else ret = skip(eof_offset); if(buffer_offset + last < eof_offset) { clear_buffer(); buffer_offset = eof_offset; } else { next = last; if(buffer_offset + last > eof_offset) throw SRC_BUG; } return ret; } bool cache::skip_relative(S_I x) { skippability dir = x >= 0 ? skip_forward : skip_backward; U_I in_cache = available_in_cache(dir); U_I abs_x = x >= 0 ? x : -x; if(is_terminated()) throw SRC_BUG; if(abs_x <= in_cache) // skipping within cache { next += x; // note that x is a *signed* integer // sanity checks if(next > last) throw SRC_BUG; return true; } else // must replace data in cache to skip { if(need_flush_write()) flush_write(); switch(dir) { case skip_forward: return skip(buffer_offset + abs_x); case skip_backward: if(buffer_offset < abs_x) return false; else return skip(buffer_offset - abs_x); default: throw SRC_BUG; } } } bool cache::truncatable(const infinint & pos) const { if(pos >= buffer_offset + last) // truncate after the cache return ref->truncatable(pos); else if(pos < buffer_offset) // truncate before the cache return ref->truncatable(pos); else // truncate in the middle of the cache { infinint max_offset = pos - buffer_offset; U_I max_off = 0; max_offset.unstack(max_off); if(!max_offset.is_zero()) throw SRC_BUG; if(first_to_write < size) // some data is write pending in the cache { if(first_to_write >= max_off) // truncate would discard all pending data and more, leading "ref" to skip backward { U_I backward = first_to_write - max_off; return ref->skippable(generic_file::skip_backward, backward) && ref->truncatable(pos); } else // no need to skip back just as current offset is less than truncate offset return ref->truncatable(pos); } else // all data was written down and we are at the "next" offset { if(next > max_off) // truncating below the last written byte, requiring skip of "ref" { U_I backward = next - max_off; return ref->skippable(generic_file::skip_backward, backward) && ref->truncatable(pos); } else // truncating after the last byte return ref->truncatable(pos); } } } void cache::inherited_read_ahead(const infinint & amount) { infinint tmp = available_in_cache(generic_file::skip_forward); if(amount > tmp) ref->read_ahead(amount - tmp); // else requested data is already in cache } U_I cache::inherited_read(char *a, U_I x_size) { U_I ret = 0; bool eof = false; infinint fallback_offset = get_position(); do { if(next >= last) // no more data to read from cache { try { if(need_flush_write()) flush_write(); } catch(...) { if(next < ret) throw SRC_BUG; // flushing failed thus cache // should contains at least ret bytes next -= ret; // this way we don't loose what has to be // written for long, while dropping the // bytes put in the cache by the current // inherited_read routine. The caller may // considering that no byte has been read // due to the exception next call will // provide the expected data throw; } try { if(x_size - ret < size) { if(eof_offset.is_zero() // we know the offset for eof || buffer_offset + last < eof_offset) // we have not all data up to eof fulfill_read(); // may fail if underlying is write_only (exception thrown) if(next >= last) // could not read anymore data eof = true; } else // reading the remaining directly from lower layer { ret += ref->read(a + ret, x_size - ret); // may fail if underlying is write_only if(ret < x_size) eof = true; clear_buffer(); // force clearing whatever is shifted_mode buffer_offset = ref->get_position(); } } catch(...) { skip(fallback_offset); throw; } } if(!eof && ret < x_size) { U_I needed = x_size - ret; U_I avail = last - next; U_I min = avail > needed ? needed : avail; if(min > 0) { (void)memcpy(a+ret, buffer + next, min); ret += min; next += min; } else throw SRC_BUG; } } while(ret < x_size && !eof); return ret; } void cache::inherited_write(const char *a, U_I x_size) { U_I wrote = 0; U_I avail, remaining; while(wrote < x_size) { avail = size - next; if(avail == 0) // we need to flush the cache { try { if(need_flush_write()) flush_write(); // may fail if underlying is read_only avail = size - next; } catch(...) { // ignoring the bytes written so far from // the given argument to inherited_write in // order to stay coherent with the view of the caller if(next < wrote) throw SRC_BUG; next -= wrote; throw; } } remaining = x_size - wrote; if(avail < remaining && !need_flush_write()) { // less data in cache than to be wrote and no write pending data in cache // we write directly to the lower layer buffer_offset += next; next = last = 0; try { ref->skip(buffer_offset); ref->write(a + wrote, remaining); // may fail if underlying is read_only or user interruption } catch(...) { infinint wrote_i = wrote; // ignoring the bytes written so far from // the given argument to inherited_write in // order to stay coherent with the view of the caller if(buffer_offset < wrote_i) throw SRC_BUG; buffer_offset -= wrote_i; ref->skip(buffer_offset); throw; } wrote = x_size; buffer_offset += remaining; } else // filling cache with data { U_I min = remaining < avail ? remaining : avail; if(first_to_write >= last) first_to_write = next; (void)memcpy(buffer + next, a + wrote, min); wrote += min; next += min; if(last < next) last = next; } } if(wrote > 0 && !eof_offset.is_zero()) eof_offset = 0; } void cache::inherited_truncate(const infinint & pos) { if(pos >= buffer_offset + last) // truncate after the cache { flush_write(); // writing pending data if any next = 0; // emptying the cache last = 0; // emptying the cache ref->truncate(pos); buffer_offset = ref->get_position(); if(buffer_offset != pos) throw SRC_BUG; } else if(pos < buffer_offset) // truncate before the cache { first_to_write = size; // dropping data pending for writing next = 0; // emptying the cache last = 0; // emptying the cache ref->truncate(pos); buffer_offset = ref->get_position(); if(buffer_offset != pos) throw SRC_BUG; } else // truncate in the middle of the cache { // we have: buffer_offset <= pos < buffer_offset + last infinint max_offset = pos - buffer_offset; U_I max_off = 0; max_offset.unstack(max_off); if(!max_offset.is_zero()) throw SRC_BUG; if(first_to_write < size) // some data was pending for writing { if(first_to_write >= max_off) // but after this truncate request there will be nothing pending for writing { U_I backward = first_to_write - max_off; first_to_write = size; // disabling first_to_write if(last > max_off) last = max_off; if(next > max_off) next = max_off; if(backward > 0) { ref->truncate(pos); if(ref->get_position() != pos) throw SRC_BUG; // position should be moved backward to the new EOF } // else nothing to do // there is no need to skip backward the // underlying object pointed to by "ref" // the truncate() only takes place in the // data pending for writing in the cache } else { // truncate takes either in the middle or // after data pending for writing if(last > max_off) last = max_off; if(next > max_off) next = max_off; // we have not to propagate the truncate() call // to the underlying layer, as it has not // yet reached this offset (or maybe if // skipping backward was performed earlier // but some data is pending to write and we // must not change the offset of the layer // below until it has not been written. } } else // all data up to "next" was written to "ref" { if(next > max_off) // truncating before the last written byte { U_I backward = next - max_off; if(last > max_off) last = max_off; if(next > max_off) next = max_off; if(backward > 0) { ref->truncate(pos); if(ref->get_position() != pos) throw SRC_BUG; // position should be moved backward to the new EOF } // else nothing to do as seen above when // first_to_write < size } else // truncating after the last written byte, we clear the cache to avoid skipping { next = 0; // emptying the cache last = 0; // emptying the cache ref->truncate(pos); buffer_offset = ref->get_position(); if(buffer_offset != pos) throw SRC_BUG; } } } } void cache::alloc_buffer(size_t x_size) { if(buffer != nullptr) throw SRC_BUG; buffer = new (nothrow) char[x_size]; if(buffer == nullptr) throw Ememory("cache::alloc_buffer"); size = x_size; half = size / 2; } void cache::release_buffer() { if(buffer == nullptr) throw SRC_BUG; delete [] buffer; buffer = nullptr; size = 0; half = 0; } void cache::shift_by_half() { U_I shift; if(last <= half) return; // not enough data to keep the cache filled by half else shift = last - half; if(next < shift) shift = next; // current position cannot be out of the buffer so we don't shift more than "next" if(first_to_write < shift) throw SRC_BUG; (void)memmove(buffer, buffer + shift, last - shift); if(first_to_write < size) first_to_write -= shift; next -= shift; last -= shift; buffer_offset += shift; } void cache::clear_buffer() { if(need_flush_write()) throw SRC_BUG; buffer_offset += next; next = last = 0; } void cache::flush_write() { if(get_mode() == gf_read_only) return; // nothing to flush // flushing the cache if(need_flush_write()) // we have something to flush { if(!ref->skip(buffer_offset + first_to_write)) throw SRC_BUG; // cannot flush write data !!! ref->write(buffer + first_to_write, last - first_to_write); } first_to_write = size; next = last; // we have wrote up to last so we must update next for clear_buffer() to work as expected if(shifted_mode) shift_by_half(); else clear_buffer(); } void cache::fulfill_read() { U_I lu; bool skipping = (last == 0); if(get_mode() == gf_write_only) return; // nothing to fill // flushing / shifting the cache contents to make room to receive new data if(shifted_mode) shift_by_half(); else clear_buffer(); /////// // some data may remain in the cache, we need to preserve them !!! // this occurres when a shift by half of the buffer has been done just before /////// if(!eof_offset.is_zero() // eof position is known && buffer_offset + last + size > eof_offset // we would read up to the eof && next == last // cache is currently empty && skipping) // we got there due to a call to skip() { infinint tmp_next; // we will read the last block size of the fill // and put it into the cache. This way backward // reading does not bring any performance penalty // when it occurs at end of file if(eof_offset > size) { // setting the value for "next" tmp_next = (buffer_offset + size) - eof_offset; // parenthesis are required to avoid substracting before addition and obtaining an // negative infinint as temporary object next = 0; tmp_next.unstack(next); if(!tmp_next.is_zero()) throw SRC_BUG; // setting the value for "buffer_offset" buffer_offset = eof_offset - size; if(!ref->skip(buffer_offset)) throw SRC_BUG; } else // file is shorter than the cache size! { // setting the value for "next" tmp_next = buffer_offset; next = 0; tmp_next.unstack(next); if(!tmp_next.is_zero()) throw SRC_BUG; // setting the value for "buffer_offset" buffer_offset = 0; if(!ref->skip(0)) throw SRC_BUG; } } else { // "next" and "buffer_offset" do not change if(!ref->skip(buffer_offset + last)) throw SRC_BUG; } lu = ref->read(buffer + last, size - last); // may fail if underlying is write_only or user aborted last += lu; } U_I cache::available_in_cache(skippability direction) const { U_I ret; switch(direction) { case skip_forward: ret = last - next; break; case skip_backward: ret = next; break; default: throw SRC_BUG; } return ret; } } // end of namespace dar-2.7.15/src/libdar/generic_file.cpp0000644000175000017500000002515114636066467014466 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" #include "infinint.hpp" #include "generic_file.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "cygwin_adapt.hpp" #include "int_tools.hpp" #include "crc.hpp" #include #include #define BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFFER_SIZE #undef BUFFER_SIZE #define BUFFER_SIZE SSIZE_MAX #endif #endif using namespace std; namespace libdar { void generic_file::terminate() { try { if(!terminated) inherited_terminate(); } catch(...) { terminated = true; throw; } terminated = true; } bool generic_file::operator == (generic_file & ref) { bool ret = true; char buffer_me[BUFFER_SIZE]; char buffer_ref[BUFFER_SIZE]; U_I lu_me; U_I lu_ref; skip(0); ref.skip(0); do { lu_me = read(buffer_me, BUFFER_SIZE); lu_ref = ref.read(buffer_ref, BUFFER_SIZE); if(lu_me != lu_ref) ret = false; else for(U_I i = 0; i < lu_me && ret; ++i) ret = buffer_me[i] == buffer_ref[i]; } while(lu_me != 0 && ret); return ret; } void generic_file::read_ahead(const infinint & amount) { if(terminated) throw SRC_BUG; if(rw == gf_write_only) throw Erange("generic_file::read", gettext("Reading ahead a write only generic_file")); else if(no_read_ahead) return; else inherited_read_ahead(amount); } U_I generic_file::read(char *a, U_I size) { if(terminated) throw SRC_BUG; if(rw == gf_write_only) throw Erange("generic_file::read", gettext("Reading a write only generic_file")); else return (this->*active_read)(a, size); } void generic_file::write(const char *a, U_I size) { if(terminated) throw SRC_BUG; if(rw == gf_read_only) throw Erange("generic_file::write", gettext("Writing to a read only generic_file")); else (this->*active_write)(a, size); } void generic_file::write(const string & arg) { if(terminated) throw SRC_BUG; if(arg.size() > int_tools_maxof_aggregate(U_I(0))) throw SRC_BUG; write(arg.c_str(), arg.size()); } S_I generic_file::read_back(char &a) { if(terminated) throw SRC_BUG; if(skip_relative(-1)) { U_I ret = read(&a,1); skip_relative(-1); return ret; } else return 0; } void generic_file::copy_to(generic_file & ref) { char buffer[BUFFER_SIZE]; U_I lu; if(terminated) throw SRC_BUG; do { try { lu = this->read(buffer, BUFFER_SIZE); } catch(Egeneric & e) { e.stack("generic_file::copy_to", "read"); throw; } if(lu > 0) { try { ref.write(buffer, lu); } catch(Egeneric & e) { e.stack("generic_file::copy_to", "write"); throw; } } } while(lu > 0); } void generic_file::copy_to(generic_file & ref, const infinint & crc_size, crc * & value) { if(terminated) throw SRC_BUG; reset_crc(crc_size); copy_to(ref); value = get_crc(); } U_32 generic_file::copy_to(generic_file & ref, U_32 size) { char buffer[BUFFER_SIZE]; S_I lu = 1, pas; U_32 wrote = 0; if(terminated) throw SRC_BUG; while(wrote < size && lu > 0) { pas = size > BUFFER_SIZE ? BUFFER_SIZE : size; try { lu = read(buffer, pas); } catch(Egeneric & e) { e.stack("generic_file::copy_to", "read"); throw; } if(lu > 0) { try { ref.write(buffer, lu); } catch(Egeneric & e) { e.stack("generic_file::copy_to", "write"); throw; } wrote += lu; } } return wrote; } infinint generic_file::copy_to(generic_file & ref, infinint size) { U_32 tmp = 0, delta; infinint wrote = 0; if(terminated) throw SRC_BUG; size.unstack(tmp); do { delta = copy_to(ref, tmp); wrote += delta; tmp -= delta; if(tmp == 0) size.unstack(tmp); } while(tmp > 0); return wrote; } bool generic_file::diff(generic_file & f, const infinint & me_read_ahead, const infinint & you_read_ahead, const infinint & crc_size, crc * & value) { infinint err_offset; return diff(f, me_read_ahead, you_read_ahead, crc_size, value, err_offset); } bool generic_file::diff(generic_file & f, const infinint & me_read_ahead, const infinint & you_read_ahead, const infinint & crc_size, crc * & value, infinint & err_offset) { char buffer1[BUFFER_SIZE]; char buffer2[BUFFER_SIZE]; U_I lu1 = 0, lu2 = 0; bool diff = false; err_offset = 0; if(terminated) throw SRC_BUG; if(get_mode() == gf_write_only || f.get_mode() == gf_write_only) throw Erange("generic_file::diff", gettext("Cannot compare files in write only mode")); skip(0); f.skip(0); read_ahead(me_read_ahead); f.read_ahead(you_read_ahead); value = create_crc_from_size(crc_size); if(value == nullptr) throw SRC_BUG; try { do { lu1 = read(buffer1, BUFFER_SIZE); lu2 = f.read(buffer2, BUFFER_SIZE); if(lu1 == lu2) { U_I i = 0; while(i < lu1 && buffer1[i] == buffer2[i]) ++i; if(i < lu1) { diff = true; err_offset += i; } else { err_offset += lu1; value->compute(buffer1, lu1); } } else { U_I min = lu1 > lu2 ? lu2 : lu1; diff = true; err_offset += min; } } while(!diff && lu1 > 0); } catch(...) { delete value; value = nullptr; throw; } return diff; } void generic_file::reset_crc(const infinint & width) { if(terminated) throw SRC_BUG; if(active_read == &generic_file::read_crc) throw SRC_BUG; // crc still active, previous CRC value never read if(checksum != nullptr) throw SRC_BUG; // checksum is only created when crc mode is activated checksum = create_crc_from_size(width); enable_crc(true); } crc *generic_file::get_crc() { crc *ret = nullptr; if(checksum == nullptr) throw SRC_BUG; else { ret = checksum; checksum = nullptr; // the CRC object is now under the responsibility of the caller } enable_crc(false); return ret; } void generic_file::truncate(const infinint & pos) { if(terminated) throw SRC_BUG; if(rw == gf_write_only || rw == gf_read_write) inherited_truncate(pos); else throw Erange("generic_file::truncate", gettext("Cannot truncate a read-only generic_file")); } void generic_file::sync_write() { if(terminated) throw SRC_BUG; if(rw == gf_write_only || rw == gf_read_write) inherited_sync_write(); else throw Erange("generic_file::sync_write", gettext("Cannot sync write on a read-only generic_file")); } void generic_file::flush_read() { if(terminated) throw SRC_BUG; if(rw == gf_read_only || rw == gf_read_write) inherited_flush_read(); else throw Erange("genercic_file::flush_read", gettext("Cannot flush read a write-only generic_file")); } void generic_file::enable_crc(bool mode) { if(terminated) throw SRC_BUG; if(mode) // routines with crc features { if(checksum == nullptr) throw SRC_BUG; active_read = &generic_file::read_crc; active_write = &generic_file::write_crc; } else { active_read = &generic_file::inherited_read; active_write = &generic_file::inherited_write; } } U_I generic_file::read_crc(char *a, U_I size) { if(terminated) throw SRC_BUG; else { S_I ret = inherited_read(a, size); if(checksum == nullptr) throw SRC_BUG; checksum->compute(a, ret); return ret; } } void generic_file::write_crc(const char *a, U_I size) { if(terminated) throw SRC_BUG; inherited_write(a, size); if(checksum == nullptr) throw SRC_BUG; checksum->compute(a, size); } void generic_file::destroy() { if(checksum != nullptr) { delete checksum; checksum = nullptr; } } void generic_file::copy_from(const generic_file & ref) { rw = ref.rw; if(ref.checksum != nullptr) checksum = ref.checksum->clone(); else checksum = nullptr; terminated = ref.terminated; no_read_ahead = ref.no_read_ahead; active_read = ref.active_read; active_write = ref.active_write; } void generic_file::move_from(generic_file && ref) noexcept { rw = move(ref.rw); swap(checksum, ref.checksum); terminated = move(ref.terminated); no_read_ahead = move(ref.no_read_ahead); active_read = move(ref.active_read); active_write = move(ref.active_write); } } // end of namespace dar-2.7.15/src/libdar/escape_catalogue.hpp0000644000175000017500000001446514636067146015345 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file escape_catalogue.hpp /// \brief class escape_catalogue definition. Used for sequential writing to archives, as well as several other inherited classes from catalogue.hpp /// \ingroup Private /// /// This class inherits from the class catalogue and implements /// the pre_add(...) method, which role is to add an escape sequence followed /// by an entry dump (usually used at the end of archive is the so called catalogue part /// of the archive). This sequence followed by entry dump is added /// before each file's data all along the archive. /// Other inherited classes, implement the escape specific part, used when performing /// sequential reading of the catalogue #ifndef ESCAPE_CATALOGUE_HPP #define ESCAPE_CATALOGUE_HPP #include "../my_config.h" #include "catalogue.hpp" #include "escape.hpp" #include "pile_descriptor.hpp" #include "smart_pointer.hpp" #include "header_version.hpp" namespace libdar { /// \addtogroup Private /// @{ class escape_catalogue : public catalogue { public: /// constructor to setup a escape_catalogue that will drop marks all along the archive and drop its content at end of archive escape_catalogue(const std::shared_ptr & dialog, const pile_descriptor & x_pdesc, const datetime & root_last_modif, const label & data_name); /// constructor to setup a escape_catalogue that will be fed by sequentially reading the archive escape_catalogue(const std::shared_ptr & dialog, ///< user interaction const pile_descriptor & x_pdesc, ///< stack descriptor where to write to const header_version & ver, ///< archive header version read const std::list & known_signatories, ///< signatories that signed the archive header, to be compared with internal catalogue when reaching the end of the archive bool lax = false ///< whether to use lax mode ); escape_catalogue(const escape_catalogue & ref) : catalogue(ref) { copy_from(ref); }; escape_catalogue(escape_catalogue && ref) = delete; escape_catalogue & operator = (const escape_catalogue &ref); escape_catalogue & operator = (escape_catalogue && ref) = delete; ~escape_catalogue() { destroy(); }; // inherited from catalogue virtual void pre_add(const cat_entree *ref, const pile_descriptor* dest) const override; virtual void pre_add_ea(const cat_entree *ref, const pile_descriptor* dest) const override; virtual void pre_add_crc(const cat_entree *ref, const pile_descriptor* dest) const override; virtual void pre_add_dirty(const pile_descriptor* dest) const override; virtual void pre_add_ea_crc(const cat_entree *ref, const pile_descriptor* dest) const override; virtual void pre_add_waste_mark(const pile_descriptor* dest) const override; virtual void pre_add_failed_mark(const pile_descriptor* dest) const override; virtual void pre_add_fsa(const cat_entree *ref, const pile_descriptor* dest) const override; virtual void pre_add_fsa_crc(const cat_entree *ref, const pile_descriptor* dest) const override; virtual void pre_add_delta_sig(const pile_descriptor* dest) const override; virtual escape *get_escape_layer() const override { return pdesc.is_null() ? nullptr : pdesc->esc; }; virtual void drop_escape_layer() override { pdesc.assign(nullptr); }; virtual void reset_read() const override; virtual void end_read() const override; virtual void skip_read_to_parent_dir() const override; virtual bool read(const cat_entree * & ref) const override; virtual bool read_if_present(std::string *name, const cat_nomme * & ref) const override; virtual void tail_catalogue_to_current_read() override; virtual bool read_second_time_dir() const override { return status == ec_detruits; }; virtual void set_in_place(const path & arg) override; virtual void clear_in_place() override; private: enum state { ec_init, ///< state in which no one file has yet been searched in the archive ec_marks, ///< state in which we find the next file using escape sequence marks ec_eod, ///< state in which the archive is missing trailing EOD entries, due to user interruption, thus returning EOD in enough number to get back to the root directory ec_signature, ///< state in which we compare inline and internal catalogues ec_detruits, ///< state in which which detruits objects are returned from the catalogue ec_completed ///< state in which the escape_catalogue object is completed and has all information in memory as a normal catalogue }; smart_pointer pdesc; header_version x_ver; std::list known_sig; bool x_lax; std::map corres; state status; catalogue *cat_det; ///< holds the final catalogue's detruit objects when no more file can be read from the archive infinint min_read_offset; ///< next offset in archive should be greater than that to identify a mark infinint depth; ///< directory depth of archive being read sequentially infinint wait_parent_depth; ///< ignore any further entry while depth is less than wait_parent_depth. disabled is set to zero void set_esc_and_stack(const pile_descriptor & x_pdesc); void copy_from(const escape_catalogue & ref); void destroy(); void merge_cat_det(); void reset_reading_process(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/int_tools.hpp0000644000175000017500000000451614636066467014074 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file int_tools.hpp /// \brief elementary operation for infinint integers /// \ingroup Private /// \note API included module due to dependencies #ifndef INT_TOOLS_HPP #define INT_TOOLS_HPP #include "../my_config.h" #include "integers.hpp" namespace libdar { /// \addtogroup Private /// @{ using int_tools_bitfield = unsigned char[8]; extern void int_tools_swap_bytes(unsigned char &a, unsigned char &b); extern void int_tools_swap_bytes(unsigned char *a, U_I size); extern void int_tools_expand_byte(unsigned char a, int_tools_bitfield &bit); extern void int_tools_contract_byte(const int_tools_bitfield &b, unsigned char & a); // integer (agregates) manipulations // argument must be a regular interger (a bit field). template extern T int_tools_rotate_right_one_bit(T v) { bool retenue = (v & 1) != 0; v >>= 1; if(retenue) v |= T(1) << (sizeof(v)*8 - 1); return v; } template extern T int_tools_maxof_aggregate(T unused) { unused = 0; unused = ~unused; unused = unused > 0 ? unused : ~int_tools_rotate_right_one_bit(T(1)); return unused; } template static B int_tools_higher_power_of_2(B val) { B i = 0; while((val >> i) > 1) i++; return i; } /// @} } #endif dar-2.7.15/src/libdar/statistics.hpp0000644000175000017500000003315014636066467014250 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file statistics.hpp /// \brief class handling access to the data summary of treated files after and during each operation /// \ingroup API #ifndef STATISTICS_HPP #define STATISTICS_HPP #include "../my_config.h" #include "infinint.hpp" #include "user_interaction.hpp" #include "deci.hpp" extern "C" { #if MUTEX_WORKS #if HAVE_PTHREAD_H #include #endif #endif } #if MUTEX_WORKS #define LOCK_IN pthread_mutex_lock(&lock_mutex) #define LOCK_OUT pthread_mutex_unlock(&lock_mutex) #define LOCK_IN_CONST pthread_mutex_lock(const_cast(&lock_mutex)) #define LOCK_OUT_CONST pthread_mutex_unlock(const_cast(&lock_mutex)) #else #define LOCK_IN // #define LOCK_OUT // #define LOCK_IN_CONST // #define LOCK_OUT_CONST // #endif namespace libdar { /// \addtogroup Private /// @{ /// class used by libdar::archive class to give a summary of treated file during and after an operation /// the different fields are used for backup, restoration and other operation /// their meaning changes a bit depending on the operation. Some operation may /// not use all fields. To have a detailed view of what fields get used and what /// are their meaning see the archive class constructor and methods documentation class statistics { public: /// constructor /// \param[in] lock whether to use mutex to manipulate (read or write) variables of that object /// \note using a statistics object built without lock (false given as argument to the constructor) may /// lead to application crash if several threads are accessing at the same object at the same time when /// at least one thread is modifying this object, unless you really know what you are doing, it is better /// to always use the default value for this constructor or to explicitely give "true" as argument. statistics(bool lock = true) { init(lock); clear(); }; /// copy constructor statistics(const statistics & ref) { copy_from(ref); }; /// move constructor statistics(statistics && ref) { init(false); move_from(std::move(ref)); }; /// copy assignement statistics & operator = (const statistics & ref) { detruit(); copy_from(ref); return *this; }; /// move assignement statistics & operator = (statistics && ref) noexcept { move_from(std::move(ref)); return *this; }; /// destructor ~statistics() { detruit(); }; /// reset counters to zero void clear(); /// total number of file treated infinint total() const; /// increment by one the treated counter void incr_treated() { (this->*increment)(&treated); }; /// increment by one the hard_links counter void incr_hard_links() { (this->*increment)(&hard_links); }; /// increment by one the skipped counter void incr_skipped() { (this->*increment)(&skipped); }; /// increment by one the inode_only counter void incr_inode_only() { (this->*increment)(&inode_only); }; /// increment by one the ignored counter void incr_ignored() { (this->*increment)(&ignored); }; /// increment by one the tooold counter void incr_tooold() { (this->*increment)(&tooold); }; /// increment by one the errored counter void incr_errored() { (this->*increment)(&errored); }; /// increment by one the deleted counter void incr_deleted() { (this->*increment)(&deleted); }; /// increment by one the ea_treated counter void incr_ea_treated() { (this->*increment)(&ea_treated); }; /// increment by one the fsa treated counter void incr_fsa_treated() { (this->*increment)(&fsa_treated); }; /// increment the ignored counter by a given value void add_to_ignored(const infinint & val) { (this->*add_to)(&ignored, val); }; /// increment the errored counter by a given value void add_to_errored(const infinint & val) { (this->*add_to)(&errored, val); }; /// increment the deleted counter by a given value void add_to_deleted(const infinint & val) { (this->*add_to)(&deleted, val); }; /// increment the byte amount counter by a given value void add_to_byte_amount(const infinint & val) { (this->*add_to)(&byte_amount, val); }; /// substract value from the treated counter void sub_from_treated(const infinint & val) { (this->*sub_from)(&treated, val); }; /// substract value to the ea_treated counter void sub_from_ea_treated(const infinint & val) { (this->*sub_from)(&ea_treated, val); }; /// substract value to the hard_links counter void sub_from_hard_links(const infinint & val) { (this->*sub_from)(&hard_links, val); }; /// substract value to the fsa_treated counter void sub_from_fsa_treated(const infinint & val) { (this->*sub_from)(&fsa_treated, val); }; /////////// // getting methods returning infinint /// returns the current value of the treated counter infinint get_treated() const { return (this->*returned)(&treated); }; /// returns the current value of the hard_links counter infinint get_hard_links() const { return (this->*returned)(&hard_links); }; /// returns the current value of the skipped counter infinint get_skipped() const { return (this->*returned)(&skipped); }; /// returns the current value of the inode_only counter infinint get_inode_only() const { return (this->*returned)(&inode_only); }; /// returns the current value of the ignored counter infinint get_ignored() const { return (this->*returned)(&ignored); }; /// returns the current value of the tooold counter infinint get_tooold() const { return (this->*returned)(&tooold); }; /// returns the current value of the errored counter infinint get_errored() const { return (this->*returned)(&errored); }; /// returns the current value of the deleted counter infinint get_deleted() const { return (this->*returned)(&deleted); }; /// returns the current value of the ea_treated counter infinint get_ea_treated() const { return (this->*returned)(&ea_treated); }; /// returns the current value of the byte_amount counter infinint get_byte_amount() const { return (this->*returned)(&byte_amount); }; /// returns the current value of the fsa_treated counter infinint get_fsa_treated() const { return (this->*returned)(&fsa_treated); }; //////////// // now the _str() variant returning std::string /// returns the current value of the treated counter as a std::string std::string get_treated_str() const { return deci(get_treated()).human(); }; /// returns the current value of the hard_links counter as a std::string; std::string get_hard_links_str() const { return deci(get_hard_links()).human(); }; /// returns the current value of the skipped counter as a std::string std::string get_skipped_str() const { return deci(get_skipped()).human(); }; /// returns the current value of the inode_only counter as a std::string std::string get_inode_only_str() const { return deci(get_inode_only()).human(); }; /// returns the current value of the ignored counter as a std::string std::string get_ignored_str() const { return deci(get_ignored()).human();}; /// returns the current value of the tooold counter as a std::string std::string get_tooold_str() const { return deci(get_tooold()).human(); }; /// returns the current value of the errored counter as a std::string std::string get_errored_str() const { return deci(get_errored()).human(); }; /// returns the current value of the deleted counter as a std::string std::string get_deleted_str() const { return deci(get_deleted()).human(); }; /// returns the current value of the ea_treated counter as a std::string std::string get_ea_treated_str() const { return deci(get_ea_treated()).human(); }; /// returns the current value of the byte_amount counter as a std::string std::string get_byte_amount_str() const { return deci(get_byte_amount()).human(); }; /// returns the current value of the fsa_treated counter as a std::string std::string get_fsa_treated_str() const { return deci(get_fsa_treated()).human(); }; /// decrement by one the treated counter void decr_treated() { (this->*decrement)(&treated); }; /// decrement by one the hard_links counter void decr_hard_links() { (this->*decrement)(&hard_links); }; /// decrement by one the skipped counter void decr_skipped() { (this->*decrement)(&skipped); }; /// decrement by one the inode_only counter void decr_inode_only() { (this->*decrement)(&inode_only); }; /// decrement by one the ignored counter void decr_ignored() { (this->*decrement)(&ignored); }; /// decrement by one the toold counter void decr_tooold() { (this->*decrement)(&tooold); }; /// decrement by one the errored counter void decr_errored() { (this->*decrement)(&errored); }; /// decrement by one the deleted counter void decr_deleted() { (this->*decrement)(&deleted); }; /// decrement by one the ea_treated counter void decr_ea_treated() { (this->*decrement)(&ea_treated); }; /// decrement by one the fsa_treated counter void decr_fsa_treated() { (this->*decrement)(&fsa_treated); } /// set to the given value the byte_amount counter void set_byte_amount(const infinint & val) { (this->*set_to)(&byte_amount, val); }; /// debuging method void dump(user_interaction & dialog) const; private: #if MUTEX_WORKS /// lock the access to the private variable of the curent object pthread_mutex_t lock_mutex; #endif /// whether we use locking or not bool locking; /// number of inode treated (saved, restored, etc.) [all operations] infinint treated; /// number of hard linked inodes treated (including those ignored by filters) infinint hard_links; /// files not changed since last backup / file not restored because not saved in backup infinint skipped; /// files which operation only affected inode metadata not its data infinint inode_only; /// ignored files due to filters infinint ignored; /// ignored files because less recent than the filesystem entry [restoration] / modfied during backup infinint tooold; /// files that could not be saved / files that could not be restored (filesystem access right) infinint errored; /// deleted file seen / number of files deleted during the operation [restoration] infinint deleted; /// number of EA saved / number of EA restored infinint ea_treated; /// auxilliary counter, holds the wasted bytes due to repeat on change feature for example. infinint byte_amount; /// number of FSA saved / number of FSA restored infinint fsa_treated; void (statistics::*increment)(infinint * var); ///< generic method for incrementing a variable void (statistics::*add_to)(infinint * var, const infinint & val); ///< generic method for add a value to a variable infinint (statistics::*returned)(const infinint * var) const; ///< generic method for obtaining the value of a variable void (statistics::*decrement)(infinint * var); ///< generic method for decrementing a variable void (statistics::*set_to)(infinint * var, const infinint & val); ///< generic method for setting a variable to a given value void (statistics::*sub_from)(infinint *var, const infinint & val);///< generic method for substracting to a variable void increment_locked(infinint * var) { LOCK_IN; (*var)++; LOCK_OUT; }; void increment_unlocked(infinint * var) { (*var)++; } void add_to_locked(infinint * var, const infinint & val) { LOCK_IN; (*var) += val; LOCK_OUT; } void add_to_unlocked(infinint *var, const infinint & val) { (*var) += val; } infinint returned_locked(const infinint * var) const { infinint ret; LOCK_IN_CONST; ret = *var; LOCK_OUT_CONST; return ret; }; infinint returned_unlocked(const infinint * var) const { return *var; }; void decrement_locked(infinint * var) { LOCK_IN; (*var)--; LOCK_OUT; } void decrement_unlocked(infinint * var) { (*var)--; } void set_to_locked(infinint *var, const infinint & val) { LOCK_IN; (*var) = val; LOCK_OUT; } void set_to_unlocked(infinint *var, const infinint & val) { *var = val; } void sub_from_unlocked(infinint *var, const infinint & val) { *var -= val; } void sub_from_locked(infinint *var, const infinint & val) { LOCK_IN; *var -= val; LOCK_OUT; } /// set locking & mutex void init(bool lock); /// release and free the mutex void detruit(); /// reset mutex and copy data from the object of reference void copy_from(const statistics & ref); /// used by to implement move related operations void move_from(statistics && ref) noexcept; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/tools.hpp0000644000175000017500000007204614636067146013220 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file tools.hpp /// \brief a set of general purpose routines /// \ingroup Private #ifndef TOOLS_HPP #define TOOLS_HPP #include "../my_config.h" extern "C" { #if STDC_HEADERS #include #endif #if HAVE_SIGNAL_H #include #endif #if HAVE_GPGME_H #include #endif } #include #include #include #include #include #include "path.hpp" #include "infinint.hpp" #include "generic_file.hpp" #include "integers.hpp" #include "tlv_list.hpp" #include "datetime.hpp" #include "entrepot.hpp" namespace libdar { /// \addtogroup Private /// @{ /// libdar internal use only: it is launched from get_version() and initializes tools internal variables extern void tools_init(); /// libdar internal use only: it is launched from close_and_clean() and releases tools internal variables extern void tools_end(); /// write a string to a file with a '\\0' at then end /// \param[in] f the file to write to /// \param[in] s the string to write to file extern void tools_write_string(generic_file & f, const std::string & s); /// read a string from a file expecting it to terminate by '\\0' /// \param f the file to read from /// \param s the string to put the data to (except the ending '\\0') extern void tools_read_string(generic_file & f, std::string & s); /// write a string to a file, '\\0' has no special meaning nor is added at the end /// \param[in] f the file to write to /// \param[in] s the string to write to file extern void tools_write_string_all(generic_file & f, const std::string & s); /// read a string if given size from a file '\\0' has no special meaning /// \param[in] f is the file to read from /// \param[in] s is the string to put read data in /// \param[in] taille is the size in byte to read extern void tools_read_string_size(generic_file & f, std::string & s, infinint taille); /// retrieve the size in byte of a file /// \param[in] p is the path to the file which size is to get /// \return the size of the file in byte extern infinint tools_get_filesize(const path &p); /// convert an integer to its decimal representation with the highest unit of metric system /// \param[in] number is the integer to convert /// \param[in] unit unit symbol (o for octet, m for meter, etc.) to apply metric system to, this may be nullptr /// \param[in] binary if set to true using the ki, Gi, Mi ... scale instead of the traditional k, G, M, ... prefixes /// \return the string representing the number in metric system (ex: "1 ko", "200 Mio", ...) extern std::string tools_display_integer_in_metric_system(infinint number, const std::string & unit, bool binary); /// give a pointer to the last character of the given value in the given string /// \param[in] s is the given string /// \param[in] v is the given char value /// \return a interator on s, pointing on the last char of s equal to v or a pointing to s.end() if no such char could be found is "s" /// \note the arguments are not modified neither the data they are pointing to. However the const statement has not been used to /// be able to return a iterator on the string (and not a const_interator). There is probably other ways to do that (using const_cast) for example extern std::string::iterator tools_find_last_char_of(std::string &s, unsigned char v); /// set blocking/not blocking mode for reading on a file descriptor /// \param[in] fd file descriptor to read on /// \param[in] mode set to true for a blocking read and to false for non blocking read extern void tools_blocking_read(int fd, bool mode); /// convert uid to name in regards to the current system's configuration /// \param[in] uid the User ID number /// \return the name of the corresponding user or the uid if none corresponds extern std::string tools_name_of_uid(const infinint & uid); /// convert gid to name in regards of the current system's configuration /// \param[in] gid the Group ID number /// \return the name of the corresponding group or the gid if none corresponds extern std::string tools_name_of_gid(const infinint & gid); /// convert unsigned word to string /// \param[in] x the unsigned word to convert /// \return the decimal representation of the given integer extern std::string tools_uword2str(U_16 x); /// convert integer to string /// \param[in] x the integer to convert /// \return the decimal representation of the given integer extern std::string tools_int2str(S_I x); extern std::string tools_uint2str(U_I x); /// prepend spaces before the given string /// \param[in] s the string to append spaces to /// \param[in] expected_size the minimum size of the resulting string /// \return a string at least as much long as expected_size with prepended leading spaces if necessary extern std::string tools_addspacebefore(std::string s, U_I expected_size); /// convert a date in second to its human readable representation /// \param[in] date the date in second /// \return the human representation corresponding to the argument extern std::string tools_display_date(const datetime & date); /// convert a string to a char * /// \param[in] x is the string to convert /// \return the address of newly allocated memory containing the equivalent string as the argument /// \exception Ememory is thrown if the memory allocation failed, this call never return nullptr /// \note Do not use this function, use std::string::c_str(). The allocated memory must be released by the caller thanks to the "delete []" operator extern char *tools_str2charptr(const std::string &x); /// convert an integer written in decimal notation to the corresponding value /// \param[in] x the decimal representation of the integer /// \return the value corresponding to the decimal representation given extern U_I tools_str2int(const std::string & x); /// wrapper to the "system" system call. /// \param[in,out] dialog for user interaction /// \param[in] argvector the equivalent to the argv[] vector extern void tools_system(user_interaction & dialog, const std::vector & argvector); /// wrapper to the "system" system call using anonymous pipe to tranmit arguments to the child process /// \param[in,out] dialog for user interaction /// \param[in] dar_cmd the path to the executable to run /// \param[in] argvpipe the list of arguments to pass through anonymous pipe /// \note the command to execute must understand the --pipe-fd option that /// gives the filedescriptor to read from the command-line options extern void tools_system_with_pipe(const std::shared_ptr & dialog, const std::string & dar_cmd, const std::vector & argvpipe); /// write a list of string to file /// \param[in] f the file to write to /// \param[in] x the list of string to write extern void tools_write_vector(generic_file & f, const std::vector & x); /// read a list of string from a file /// \param[in] f the file to read from /// \param[out] x the list to fill from file extern void tools_read_vector(generic_file & f, std::vector & x); /// concatenate a vectors of strings in a single string /// \param[in] separator string to insert between two elements /// \param[in] x the list string /// \return the result of the concatenation of the members of the list with separtor between two consecutive members extern std::string tools_concat_vector(const std::string & separator, const std::vector & x); /// concatenate two vectors /// \param[in] a the first vector /// \param[in] b the second vector /// \return a vector containing the elements of a and the element of b std::vector operator + (std::vector a, std::vector b); /// test if two dates are equal taking care of a integer hour of difference /// \param[in] hourshift is the number of integer hour more or less two date can be considered equal /// \param[in] date1 first date to compare /// \param[in] date2 second date to compare to /// \return whether dates are equal or not extern bool tools_is_equal_with_hourshift(const infinint & hourshift, const datetime & date1, const datetime & date2); /// template function to add two vectors template std::vector operator +=(std::vector & a, const std::vector & b) { a = a + b; return a; } /// returns the file pointed to by a symbolic link (or transparent if the file is not a symlink). /// \param root the path to the file to read /// \return the file pointed to by the symlink or the value given in argument if it is not a symlink /// \note an exception can occur if lack of memory or invalid argument given (nullptr or empty string), system call error... extern std::string tools_readlink(const char *root); /// set dates of a given file, no exception thrown /// \param[in] chem the path to the file to set /// \param[in] symlink true if the file is a symlink /// \param[in] last_acc last access date to use /// \param[in] last_mod last modification date to use /// \param[in] birth creation date of the file, if not known, use the value of last_mod for efficiency extern void tools_noexcept_make_date(const std::string & chem, bool symlink, const datetime & last_acc, const datetime & last_mod, const datetime & birth); /// set dates of a given file, may throw exception /// \param[in] chemin the path to the file to set /// \param[in] symlink true if the file is a symlink /// \param[in] access last access date to use /// \param[in] modif last modification date to use /// \param[in] birth time of creation of the file /// \note if birth time is not known, it should be set to the value of modif for efficiency extern void tools_make_date(const std::string & chemin, bool symlink, const datetime & access, const datetime & modif, const datetime & birth); /// compare two string in case insensitive manner /// \param[in] a first string to compare /// \param[in] b second string to compare /// \return whether the two string given in argument are equal in case insensitive comparison extern bool tools_is_case_insensitive_equal(const std::string & a, const std::string & b); /// \brief convert a string to upper case /// /// \param[in] r the string to convert /// \param[out] uppered resulting upper cased string /// \note in case of invalid wide char met in source string, the upper case convertion /// is done in ASCII mode (byte by byte) extern void tools_to_upper(const std::string & r, std::string & uppered); #if HAVE_WCTYPE_H /// \brief convert a wstring to upper case /// /// \param[in,out] r to convert /// \note wstring is a string of wchar_t (wide-char) type used to store /// on variable lenght of byte sequence the many characters defined with UTF /// like cirillic and greek letters. extern void tools_to_wupper(std::wstring & r); #endif /// make printf-like formating to a std::string /// \param[in] format the format string /// \param[in] ... list of argument to use against the format string /// \return the resulting string /// \note the supported masks for the format are: /// - \%s \%c \%d \%o \%\% (usual behavior) /// - \%x display an integer under hexadecimal notation /// - \%i (matches infinint *) /// - \%S (matches std::string *) /// . extern std::string tools_printf(const char *format, ...); /// make printf-like formating to a std::string /// \param[in] format the format string /// \param[in] ap list of argument to use against the format string /// \return the resulting string /// \note the supported masks for the format are: /// - \%s \%c \%d \%\% (normal behavior) /// - \%i (matches infinint *) /// - \%S (matches std::string *) /// . extern std::string tools_vprintf(const char *format, va_list ap); /// test the presence of files corresponding to a given mask in a directory (regex mask) /// \param[in] ent entrepot where to read filesystem from /// \param[in] file_mask regex expression which designates the files to look for /// \return true if some files have found matching the file_mask extern bool tools_do_some_files_match_mask_regex(const entrepot & ent, const std::string & file_mask); /// remove files from a given directory /// \param[in,out] dialog for user interaction /// \param[in] ent entrepot where to remove files from /// \param[in] file_mask regex expression which designates the files to remove /// \param[in] info_details whether user must be displayed details of the operation /// \note This is equivalent to the 'rm' command with regex expression in place of glob one extern void tools_unlink_file_mask_regex(user_interaction & dialog, const entrepot & ent, const std::string & file_mask, bool info_details); /// prevents slice overwriting: check the presence of slice and if necessary ask the user if they can be removed /// \param[in,out] dialog for user interaction /// \param[in] ent entrepot where to check for slice overwriting /// \param[in] basename is the archive basename /// \param[in] extension is the archive filename extension /// \param[in] info_details whether user must be displayed details of the operation /// \param[in] allow_overwriting whether overwriting is allowed by the user /// \param[in] warn_overwriting whether a warning must be issued before overwriting (if allowed) /// \param[in] dry_run do a dry-run exection (no filesystem modification is performed) /// \note may thow exceptions. extern void tools_avoid_slice_overwriting_regex(user_interaction & dialog, const entrepot & ent, const std::string & basename, const std::string & extension, bool info_details, bool allow_overwriting, bool warn_overwriting, bool dry_run); /// tells whether two files are on the same mounted filesystem /// \param[in] file1 first file /// \param[in] file2 second file /// \return true if the two files are located under the same mounting point /// \note if one of the file is not present or if the filesystem information /// is not possible to be read an exception is throw (Erange) extern bool tools_are_on_same_filesystem(const std::string & file1, const std::string & file2); /// transform a relative path to an absolute one given the current directory value /// \param[in] src the relative path to transform /// \param[in] cwd the value to take for the current directory /// \return the corresponding absolute path extern path tools_relative2absolute_path(const path & src, const path & cwd); /// block all signals (based on POSIX sigprocmask) /// \param[out] old_mask is set to the old mask value (for later unmasking signals) /// \exception Erange is thrown if system call failed for some reason extern void tools_block_all_signals(sigset_t &old_mask); /// unblock signals according to given mask /// \param[in] old_mask value to set to blocked signal mask /// \exception Erange is thrown if system call failed for some reason extern void tools_set_back_blocked_signals(sigset_t old_mask); /// counts the number of a given char in a given string /// \param[in] s string to look inside of /// \param[in] a char to look for /// \return the number of char found extern U_I tools_count_in_string(const std::string & s, const char a); /// returns the last modification date of the given file /// \param[in,out] dialog for user interaction /// \param[in] s path of the file to get the last mtime /// \param[in] auto_zeroing whether to just warn instead of asking user confirmation /// \param[in] silent if set do not warn nor ask /// \param[in] ignored_as_symlink for inodes present in this set, /// get the symlink pointed to date rather than the date of they symlink itself /// (no impact if the inode is not a symlink) /// \return the mtime of the given file extern datetime tools_get_mtime(user_interaction & dialog, const std::string & s, bool auto_zeroing, bool silent, const std::set & ignored_as_symlink = std::set()); /// returns the size of the given plain file /// \param[in] s path of the file to get the size /// \return the size if the file in byte extern infinint tools_get_size(const std::string & s); /// convert the given string to infinint taking care of multiplication suffixes like k, M, T, etc. /// \param[in] s is the string to read /// \param[in] base is the multiplication factor (base = 1000 for SI, base = 1024 for computer science use) /// \return the value encoded in the given string extern infinint tools_get_extended_size(std::string s, U_I base); /// produce the string resulting from the substition of % macro defined in the map /// \param[in] hook is the user's expression in which to proceed to substitution /// \param[in] corres is a map telling which char following a % sign to replace by which string /// \return the resulting string of the substitution extern std::string tools_substitute(const std::string & hook, const std::map & corres); /// produces the string resulting from the substitution of %... macro /// \param[in] hook the string in which to substitute /// \param[in] path is by what %p will be replaced /// \param[in] basename is by what %b will be replaced /// \param[in] num is by what %n will be replaced /// \param[in] padded_num is by what %N will be replaced /// \param[in] ext is by what %e will be replaced /// \param[in] context is by what %c will be replaced /// \param[in] base_url remote repository base URL /// \return the substitued resulting string /// \note it now relies on tools_substitue extern std::string tools_hook_substitute(const std::string & hook, const std::string & path, const std::string & basename, const std::string & num, const std::string & padded_num, const std::string & ext, const std::string & context, const std::string & base_url); /// execute and retries at user will a given command line /// \param[in] ui which way to ask the user whether to continue upon command line error /// \param[in] cmd_line the command line to execute extern void tools_hook_execute(user_interaction & ui, const std::string & cmd_line); /// subsititue and execute command line /// \param[in,out] ui this is the way to contact the user /// \param[in] hook the string in which to substitute /// \param[in] path is by what %p will be replaced /// \param[in] basename is by what %b will be replaced /// \param[in] num is by what %n will be replaced /// \param[in] padded_num is by what %N will be replaced /// \param[in] ext is by what %e will be replaced /// \param[in] context is by what %c will be replaced /// \param[in] base_url remote repository base URL extern void tools_hook_substitute_and_execute(user_interaction & ui, const std::string & hook, const std::string & path, const std::string & basename, const std::string & num, const std::string & padded_num, const std::string & ext, const std::string & context, const std::string & base_url); /// convert string for xml output /// \note any < > & quote and double quote are replaced by adequate sequence for unicode /// \note second point, nothing is done here to replace system native strings to unicode extern std::string tools_output2xml(const std::string & src); /// convert octal string to integer /// \param perm is a string representing a number in octal (string must have a leading zero) /// \return the corresponding value as an integer extern U_I tools_octal2int(const std::string & perm); /// convert a number to a string corresponding to its octal representation /// \param perm is the octal number /// \return the corresponding octal string extern std::string tools_int2octal(const U_I & perm); /// change the libdar internal type representation to the usual unix char type extern char tools_cast_type_to_unix_type(char type); /// convert a permission number into its string representation (rwxrwxrwx) extern std::string tools_get_permission_string(char type, U_32 perm, bool hard); /// change the permission of the file which descriptor is given /// \param[in] fd file's descriptor /// \param[in] perm file permission to set the file to extern void tools_set_permission(S_I fd, U_I perm); /// obtain the permission of the file which descriptor is given /// \param[in] fd file's descriptor /// \return permission of the given file /// \note in case of error exception may be thrown extern U_I tools_get_permission(S_I fd); /// change ownership of the file which descriptor is given /// convert string user name or uid to numeric uid value /// \param[in] user string username /// \return uid value extern uid_t tools_ownership2uid(const std::string & user); /// convert string group name or gid to numeric gid value /// \param[in] group string username /// \return uid value extern uid_t tools_ownership2gid(const std::string & group); /// change ownership of the file which descriptor is given /// \param[in] filedesc file's descriptor /// \param[in] slice_user the user to set the file to. For empty string, no attempts to change the user ownership is done /// \param[in] slice_group the group to set the file to. For empty string, no attempts to change the group ownership is done /// \note this call may throw Erange exception upon system error extern void tools_set_ownership(S_I filedesc, const std::string & slice_user, const std::string & slice_group); /// Produces in "dest" the XORed value of "dest" and "src" /// \param[in,out] dest is the area where to write down the result /// \param[in] src points to vector or array of values to convert /// \param[in] n is the number of byte to convert from src to dest /// \note dest *must* be a valid pointer to an allocated memory area of at least n bytes extern void tools_memxor(void *dest, const void *src, U_I n); /// Produces a list of TLV from a constant type and a list of string /// \param[in,out] dialog for user interaction /// \param[in] type is the type each TLV will have /// \param[in] data is the list of string to convert into a list of TLV /// \return a tlv_list object. Each TLV in the list correspond to a string in the given list extern tlv_list tools_string2tlv_list(user_interaction & dialog, const U_16 & type, const std::vector & data); /// Produces a pseudo random number x, where 0 <= x < max /// \param[in] max defines the range of the random number to return /// \return the returned value ranges from 0 (zero) up to max (= including max) extern U_I tools_pseudo_random(U_I max); /// Template for the decomposition of any number in any base (decimal, octal, hexa, etc.) /// \param[in] number is the number to decompose /// \param[in] base is the base to decompose the number into /// \return a vector of 'digit' int the specified base, the first beeing the less significative /// \note this template does not take care of the possibily existing optimized euclide division to speed up the operation /// like what exists for infinint. A specific overriden fonction for this type would be better. /// \note, the name "big_endian" is erroneous, it gives a little endian vector template std::deque tools_number_base_decomposition_in_big_endian(N number, const B & base) { std::deque ret; if(base <= 0) throw Erange("tools_number_decoupe_in_big_endian", "base must be strictly positive"); while(number != 0) { ret.push_back(number % base); number /= base; } return ret; } /// convert a unsigned char into its hexa decima representation /// \param[in] x is the byte to convert /// \return the string representing the value of x written in hexadecimal extern std::string tools_unsigned_char_to_hexa(unsigned char x); /// convert a string into its hexadecima representation /// \param[in] input input string to convert /// \return a string containing an hexadecimal number corresponding to the bytes of the input string extern std::string tools_string_to_hexa(const std::string & input); /// Defines the CRC size to use for a given filesize /// \param[in] size is the size of the file to protect by CRC /// \return crc_size is the size of the crc to use extern infinint tools_file_size_to_crc_size(const infinint & size); /// get current working directory extern std::string tools_getcwd(); /// return the string about compression ratio extern std::string tools_get_compression_ratio(const infinint & storage_size, const infinint & file_size, bool compressed); /// wrapper routine to strerror_r extern std::string tools_strerror_r(int errnum); #ifdef GPGME_SUPPORT /// wrapper routint to gpgme_strerror_r extern std::string tools_gpgme_strerror_r(gpgme_error_t err); #endif #if HAVE_WCHAR_H /// convert a std::string to std::wstring (wide-string, aka string of wchar_t) extern std::wstring tools_string_to_wstring(const std::string & val); /// convert a std::wstring to std::string extern std::string tools_wstring_to_string(const std::wstring & val); #endif /// display the content of a secu_string, this function is only for trouble shooting! extern void tools_secu_string_show(user_interaction & dialog, const std::string & msg, const secu_string & key); template T tools_max(T a, T b) { return a > b ? a : b; } template T tools_min(T a, T b) { return a > b ? b : a; } /// remove from filesystem the file which path is given void tools_unlink(const std::string & filename); /// escape with a anti-slash character a set of chars found in the given string extern std::string tools_escape_chars_in_string(const std::string & val, const char *to_escape); /// convert an infinint to U_64 (aka "uint64_t" or yet "unsigned long long") /// \note: if the infinint is too large to fit in an U_64 it returns false extern bool tools_infinint2U_64(infinint val, U_64 & res); /// ascii to integer conversion /// \param[in] a is the ascii string to convert /// \param[out] val is the resulting value /// \return true if the conversion could be done false if the given string does not /// correspond to the decimal representation of an unsigned integer /// \note this call is now a warapper around line_tools_str2int extern bool tools_my_atoi(const char *a, U_I & val); /// convert a double (float) to infinint (integer) taking care of rounding it to the closest value extern infinint tools_double2infinint(double arg); /// check the value is not negative, and if asked set it to zero ///\param[in,out] val variable which value to check ///\param[in,out] ui for user interaction if necessary ///\param[in] inode_path to the inode for message info ///\param[in] nature type of the date/time (mtime,atime,ctime,birthtime,...) ///\param[in] ask_before whether to just warn or ask user for confirmation ///\param[in] silent if set, do not warn nor ask template void tools_check_negative_date(T & val, user_interaction & ui, const char *inode_path, const char *nature, bool ask_before, bool silent) { if(val < 0) { if(!silent) { std::string msg = tools_printf(gettext("Found negative date (%s) for inode %s ."), nature, inode_path); if(ask_before) ui.pause(tools_printf(gettext("%S Can we read it as if it was zero (1st January 1970 at 00:00:00 UTC)?"), &msg)); else // just warn ui.message(msg + gettext("Considering date as if it was zero (Jan 1970)")); } val = 0; } } /// compute an approximate log2 of the argument extern infinint tools_upper_rounded_log2(const infinint & ref); /// compute an approximate exp2 of the argument extern infinint tools_lower_rounded_exp2(const infinint & ref); /// compute an approximate square root of the argument extern infinint tools_rounded_square_root(const infinint & ref); /// compute an approximate cube root of the argument extern infinint tools_rounded_cube_root(const infinint & ref); /// @} } /// end of namespace #endif dar-2.7.15/src/libdar/i_archive.hpp0000644000175000017500000004364714636067146014016 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file i_archive.hpp /// \brief the archive class implementation is defined in this module /// \ingroup Private #ifndef I_ARCHIVE_HPP #define I_ARCHIVE_HPP #include "../my_config.h" #include #include #include #include "erreurs.hpp" #include "path.hpp" #include "statistics.hpp" #include "archive_options.hpp" #include "pile.hpp" #include "list_entry.hpp" #include "crypto.hpp" #include "slice_layout.hpp" #include "mem_ui.hpp" #include "archive_summary.hpp" #include "archive_listing_callback.hpp" #include "catalogue.hpp" #include "archive.hpp" #include "header_version.hpp" namespace libdar { /// \addtogroup API /// @{ /// the archive::i_archive class implements the most general operations on archives class archive::i_archive: public mem_ui { public: /// this constructor opens an already existing archive (for reading) [this is the "read" constructor] i_archive(const std::shared_ptr & dialog, const path & chem, const std::string & basename, const std::string & extension, const archive_options_read & options); /// this constuctor create an archive (full or differential) [this is the "create" constructor] i_archive(const std::shared_ptr & dialog, const path & fs_root, const path & sauv_path, const std::string & filename, const std::string & extension, const archive_options_create & options, statistics * progressive_report); /// this constructor builds an archive from two given archive [this is the "merge" constructor] i_archive(const std::shared_ptr & dialog, const path & sauv_path, std::shared_ptr ref_arch1, const std::string & filename, const std::string & extension, const archive_options_merge & options, statistics * progressive_report); /// this constructor create a new archive from a damaged one [this is the "repair" constructor] i_archive(const std::shared_ptr & dialog, const path & chem_src, const std::string & basename_src, const std::string & extension_src, const archive_options_read & options_read, const path & chem_dst, const std::string & basename_dst, const std::string & extension_dst, const archive_options_repair & options_repair); /// copy constructor (not implemented, throw an exception if called explicitely or implicitely) i_archive(const i_archive & ref) = delete; i_archive(i_archive && ref) = delete; i_archive & operator = (const i_archive & ref) = delete; i_archive & operator = (i_archive && ref) = delete; /// the destructor ~i_archive() { free_mem(); }; /// extraction of data from an archive statistics op_extract(const path &fs_root, const archive_options_extract & options, statistics *progressive_report); /// display a summary of the archive void summary(); /// same information as summary() but as broken out data archive_summary summary_data(); /// listing of the archive contents void op_listing(archive_listing_callback callback, void *context, const archive_options_listing & options) const; /// archive comparison with filesystem statistics op_diff(const path & fs_root, const archive_options_diff & options, statistics * progressive_report); /// test the archive integrity statistics op_test(const archive_options_test & options, statistics * progressive_report); /// this methodes isolates the catalogue of a the current archive into a separated archive void op_isolate(const path &sauv_path, const std::string & filename, const std::string & extension, const archive_options_isolate & options); /// getting information about a given directory bool get_children_of(archive_listing_callback callback, void *context, const std::string & dir, bool fetch_ea = false); /// getting information about the given directory (alternative to get_children_of) const std::vector get_children_in_table(const std::string & dir, bool fetch_ea = false) const; /// returns true if the pointed directory has one or more subdirectories bool has_subdirectory(const std::string & dir) const; /// retrieving statistics about archive contents const entree_stats get_stats() const { if(cat == nullptr) throw SRC_BUG; return cat->get_stats(); }; /// retrieving signature information about the archive const std::list & get_signatories() const { return gnupg_signed; }; /// necessary to get the catalogue fully loaded in memory in any situation /// in particular in sequential reading mode void init_catalogue() const; /// gives access to internal catalogue (not to be used from the API) const catalogue & get_catalogue() const; /// closes all filedescriptors and associated even when in sequential read mode void drop_all_filedescriptors(); /// change all inode as unsaved (equal to differential backup with no change met) void set_to_unsaved_data_and_FSA() { if(cat == nullptr) throw SRC_BUG; cat->set_to_unsaved_data_and_FSA(); }; /// returns the slice layout of the archive, or of the archive of reference in case of isolated catalogue bool get_catalogue_slice_layout(slice_layout & slicing) const; /// get the first slice header U_64 get_first_slice_header_size() const; /// get the non first slice header U_64 get_non_first_slice_header_size() const; private: enum operation { oper_create, oper_isolate, oper_merge, oper_repair }; pile stack; ///< the different layer through which the archive contents is read or wrote header_version ver; ///< information for the archive header catalogue *cat; ///< archive contents infinint local_cat_size; ///< size of the catalogue on disk bool exploitable; ///< is false if only the catalogue is available (for reference backup or isolation). bool lax_read_mode; ///< whether the archive has been openned in lax mode (unused for creation/merging/isolation) bool sequential_read; ///< whether the archive is read in sequential mode std::list gnupg_signed; ///< list of signature found in the archive (reading an existing archive) slice_layout slices; ///< slice layout, archive is not sliced <=> first_size or other_size fields are set to zero (in practice both are set to zero, but one being set is enought to determine the archive is not sliced) void free_mem(); void check_gnupg_signed() const; const catalogue & get_cat() const { if(cat == nullptr) throw SRC_BUG; else return *cat; }; const header_version & get_header() const { return ver; }; bool get_sar_param(infinint & sub_file_size, infinint & first_file_size, infinint & last_file_size, infinint & total_file_number); std::shared_ptr get_entrepot(); ///< this method may return nullptr if no entrepot is used (pipes used for archive building, etc.) infinint get_level2_size(); infinint get_cat_size() const { return local_cat_size; }; statistics op_create_in(operation op, const path & fs_root, const std::shared_ptr & sauv_path_t, archive *ref_arch, const mask & selection, const mask & subtree, const std::string & filename, const std::string & extension, bool allow_over, bool warn_over, bool info_details, bool display_treated, bool display_treated_only_dir, bool display_skipped, bool display_finished, const infinint & pause, bool empty_dir, compression algo, U_I compression_level, U_I compression_block_size, const infinint & file_size, const infinint & first_file_size, const mask & ea_mask, const std::string & execute, crypto_algo crypto, const secu_string & pass, U_32 crypto_size, const std::vector & gnupg_recipients, const std::vector & gnupg_signatories, const mask & compr_mask, const infinint & min_compr_size, bool nodump, const std::string & exclude_by_ea, const infinint & hourshift, bool empty, bool alter_atime, bool furtive_read_mode, const filesystem_ids & same_fs, comparison_fields what_to_check, bool snapshot, bool cache_directory_tagging, const infinint & fixed_date, const std::string & slice_permission, const infinint & repeat_count, const infinint & repeat_byte, bool add_marks_for_sequential_reading, bool security_check, const infinint & sparse_file_min_size, const std::string & user_comment, hash_algo hash, const infinint & slice_min_digits, const std::string & backup_hook_file_execute, const mask & backup_hook_file_mask, bool ignore_unknown, const fsa_scope & scope, U_I multi_threaded_crypto, U_I multi_threaded_compress, bool delta_signature, bool build_delta_sig, const mask & delta_mask, const infinint & delta_sig_min_size, bool delta_diff, bool zeroing_neg_date, const std::set & ignored_symlinks, modified_data_detection mod_data_detect, const infinint & iteration_count, hash_algo kdf_hash, const delta_sig_block_size & sig_block_len, statistics * progressive_report); void op_create_in_sub(operation op, ///< the filter operation to bind to const path & fs_root, ///< root of the filesystem to act on const std::shared_ptr & sauv_path_t, ///< where to create the archive catalogue * ref_cat1, ///< catalogue of the archive of reference, (cannot be nullptr if ref_cat2 is not nullptr) const catalogue * ref_cat2, ///< secondary catalogue used for merging, can be nullptr if not used bool initial_pause, ///< whether we shall pause before starting the archive creation const mask & selection, ///< filter on filenames const mask & subtree, ///< filter on directory tree and filenames const std::string & filename, ///< basename of the archive to create const std::string & extension, ///< extension of the archives bool allow_over, ///< whether to allow overwriting (of slices) const crit_action & overwrite, ///< whether and how to allow overwriting (for files inside the archive) bool warn_over, ///< whether to warn before overwriting bool info_details, ///< whether to display detailed informations bool display_treated, ///< whether to display treated files bool display_treated_only_dir, ///< whether to only display current directory of treated files bool display_skipped, ///< display skipped files for the operation bool display_finished, ///< display space and compression ratio summary for each completed directory const infinint & pause, ///< whether to pause between slices bool empty_dir, ///< whether to store excluded dir as empty directories compression algo, ///< compression algorithm U_I compression_level, ///< compression level (range 1 to 9) U_I compression_block_size, ///< compression block size (0 for normal/legacy compression mode) const infinint & file_size, ///< slice size const infinint & first_file_size, ///< first slice size const mask & ea_mask, ///< Extended Attribute to consider const std::string & execute, ///< Command line to execute between slices crypto_algo crypto, ///< crypt algorithm const secu_string & pass, ///< password ("" for onfly request of password) U_32 crypto_size, ///< size of crypto blocks const std::vector & gnupg_recipients, ///< list of email recipients to encrypted a randomly chosen key inside the archive const std::vector & gnupg_signatories, ///< list of email recipients to use for signature const mask & compr_mask, ///< files to compress const infinint & min_compr_size, ///< file size under which to not compress files bool nodump, ///< whether to consider the "nodump" filesystem flag const std::string & exclude_by_ea,///< if not empty the ea to use for inode exclusion from backup operation const infinint & hourshift, ///< hourshift (see man page -H option) bool empty, ///< whether to make an "dry-run" execution bool alter_atime, ///< whether to alter atime date (by opposition to ctime) when reading files bool furtive_read_mode, ///< whether to neither alter atime nor ctome (if true alter_atime is ignored) const filesystem_ids & same_fs, ///< confin the files consideration to the given set of filesystem comparison_fields what_to_check, ///< fields to consider wien comparing inodes (see comparison_fields enumeration) bool snapshot, ///< make as if all file had not changed bool cache_directory_tagging, ///< avoid saving directory which follow the cache directory tagging bool keep_compressed, ///< keep file compressed when merging const infinint & fixed_date, ///< whether to ignore any archive of reference and only save file which modification is more recent that the given "fixed_date" date const std::string & slice_permission, ///< permissions of slices that will be created const infinint & repeat_count, ///< max number of retry to save a file that have changed while it was read for backup const infinint & repeat_byte, ///< max amount of wasted data used to save a file that have changed while it was read for backup bool decremental, ///< in the merging context only, whether to build a decremental backup from the two archives of reference bool add_marks_for_sequential_reading, ///< whether to add marks for sequential reading bool security_check, ///< whether to check for ctime change with no reason (rootkit ?) const infinint & sparse_file_min_size, ///< starting which size to consider looking for holes in sparse files (0 for no detection) const std::string & user_comment, ///< user comment to put in the archive hash_algo hash, ///< whether to produce hash file, and which algo to use const infinint & slice_min_digits, ///< minimum digit for slice number const std::string & backup_hook_file_execute, ///< command to execute before and after files to backup const mask & backup_hook_file_mask, ///< files elected to have a command executed before and after their backup bool ignore_unknown, ///< whether to warn when an unknown inode type is met const fsa_scope & scope, ///< FSA scope for the operation U_I multi_threaded_crypto, ///< whether libdar is allowed to spawn several thread to possibily work faster on multicore CPU U_I multi_threaded_compress, ///< neeed compression_block_size > 0 to use several threads for compression/decompression bool delta_signature, ///< whether to calculate and store binary delta signature for each saved file bool build_delta_sig, ///< whether to rebuild delta sig accordingly to delta_mask const mask & delta_mask, ///< which files to consider delta signature for const infinint & delta_sig_min_size, ///< minimum file size for which to calculate delta signature bool delta_diff, ///< whether to allow delta diff backup when delta sig is present bool zeroing_neg_date, ///< if true just warn before zeroing neg date, dont ask user const std::set & ignored_symlinks, ///< list of symlink pointed to directory to recurse into modified_data_detection mod_data_detect, ///< how to verify data has not changed upon inode metadata change const infinint & iteration_count, ///< for key derivation hash_algo kdf_hash, ///< hash used for key derivation const delta_sig_block_size & sign_block_len, ///< block len for signature statistics * st_ptr ///< statistics must not be nullptr ! ); void disable_natural_destruction(); void enable_natural_destruction(); const label & get_layer1_data_name() const; const label & get_catalogue_data_name() const; bool only_contains_an_isolated_catalogue() const; ///< true if the current archive only contains an isolated catalogue void check_against_isolation(bool lax) const; ///< throw Erange exception if the archive only contains an isolated catalogue const cat_directory *get_dir_object(const std::string & dir) const; void load_catalogue(); }; } // end of namespace #endif dar-2.7.15/src/libdar/cache.hpp0000644000175000017500000001216214636066467013121 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cache.hpp /// \brief contains the cache class /// \ingroup Private #ifndef CACHE_HPP #define CACHE_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the cache class implements a fixed length read/write caching mechanism /// it is intended to reduce context switches when no compression is used /// and when reading or writing catalogue through a pipe. The catalogue /// read and write is done by calling dump/constructor methods of the many /// objects that a catalogue contains. This makes a lot of small reads or /// writes, which make very poor performances when used over the network /// through a pipe to ssh. When compression is used, the problem disapears /// as the compression engine gather these many small reads or writes into /// much bigger ones. This in only when there is no compression or encryption /// that this class is useful (and used). /// Another target of class cache is to provide limited skippability when /// data is read of written to pipe (which do not have any skippability) class cache : public generic_file { public: cache(generic_file & hidden, ///< is the file to cache, it is never deleted by the cache object, bool shift_mode, ///< if true, when all cached data has been read, half of the data is flushed from the cache, the other half is shifted and new data take place to fill the cache. This is necessary for sequential reading, but has some CPU overhead. U_I size = 102400 ///< is the (fixed) size of the cache ); cache(const cache & ref) = delete; cache(cache && ref) = delete; cache & operator = (const cache & ref) = delete; cache & operator = (cache && ref) = delete; ~cache(); void change_to_read_write() { if(get_mode() == gf_read_only) throw SRC_BUG; set_mode(gf_read_write); }; // inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override; virtual infinint get_position() const override { return buffer_offset + next; }; protected: // inherited from generic_file virtual void inherited_read_ahead(const infinint & amount) override; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override { flush_write(); }; virtual void inherited_flush_read() override { flush_write(); clear_buffer(); }; virtual void inherited_terminate() override { flush_write(); }; private: generic_file *ref; ///< underlying file, (not owned by "this', not to be delete by "this") char *buffer; ///< data in transit U_I size; ///< allocated size U_I half; ///< precalculated half = size / 2 U_I next; ///< next to read or next place to write to U_I last; ///< first byte of invalid data in the cache. we have: next <= last < size U_I first_to_write; ///< position of the first byte that need to be written. if greater than last, no byte need writing infinint buffer_offset; ///< position of the first byte in buffer bool shifted_mode; ///< whether to half flush and shift or totally flush data infinint eof_offset; ///< size of the underlying file (read-only mode), set to zero if unknown bool need_flush_write() const { return first_to_write < last; }; void alloc_buffer(size_t x_size); ///< allocate x_size byte in buffer field and set size accordingly void release_buffer(); ///< release memory set buffer to nullptr and size to zero void shift_by_half(); void clear_buffer(); void flush_write(); void fulfill_read(); U_I available_in_cache(skippability direction) const; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/integers.hpp0000644000175000017500000000713014636066467013675 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file integers.hpp /// \brief are defined here basic integer types that tend to be portable /// \ingroup API #ifndef INTEGERS_HPP #define INTEGERS_HPP #include "../my_config.h" #include /// \addtogroup API /// @{ #ifndef OS_BITS #if HAVE_INTTYPES_H extern "C" { #if HAVE_INTTYPES_H #include #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" namespace libdar { using U_8 = unsigned char; using U_16 = uint16_t; using U_32 = uint32_t; using U_64 = uint64_t; using U_I = size_t; // configure will define size_t as "unsigned int" if it not defined by system headers // thus using U_I we are sure we can compare buffer sizes with SSIZE_MAX using S_8 = signed char; using S_16 = int16_t; using S_32 = int32_t; using S_64 = int64_t; using S_I = signed int; } #else // HAVE_INTTYPES_H #error "Cannot determine interger types, use --enable-os-bits=... with the 'configure' script according to your system's CPU register size" #endif // HAVE_INTTYPES_H #else // OS_BITS is defined #if OS_BITS == 32 namespace libdar { using U_8 = unsigned char; using U_16 = unsigned short; using U_32 = unsigned long; using U_64 = unsigned long long; using U_I = size_t; using S_8 = signed char; using S_16 = signed short; using S_32 = signed long; using S_64 = signed long long; using S_I = signed int; } #else // OS_BITS != 32 #if OS_BITS == 64 namespace libdar { using U_8 = unsigned char; using U_16 = unsigned short; using U_32 = unsigned int; using U_64 = unsigned long long; using U_I = size_t; using S_8 = signed char; using S_16 = signed short; using S_32 = signed int; using S_64 = signed long long; using S_I = signed int; } #else // OS_BITS != 32 and OS_BITS != 64 #error "unknown value given to --enable-os-bits=... check the 'configure' script arguments" // unknown OS_BITS value ! use --enable-os-bits=... option to configure script // // the previous line should not compile, this is the expected behaviour #endif // OS_BITS == 64 #endif // OS_BITS == 32 #endif // OS_BITS not defined namespace libdar { /// checks sign and width of integer types /// \note this call may throws an Ehardware exception void integer_check(); /// returns true if the system is big endian, false else /// \note this call may throw an Ehardware() exception if the /// system is not coherent for all integer types bool integers_system_is_big_endian(); } /// @} #endif // header file multiple inclusion protection dar-2.7.15/src/libdar/integers.cpp0000644000175000017500000001143714636066467013675 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author : dar.linux@free.fr /*********************************************************************/ #include "../my_config.h" #include "integers.hpp" #include "erreurs.hpp" #include "tools.hpp" namespace libdar { // template to check the width of a particular integer type template void integer_check_width(const char *type_name, T x, unsigned int expected) { if(sizeof(T) != expected) throw Ehardware("interger_check_width", tools_printf(gettext("%s type length is not %d byte(s) but %d"), type_name, expected, sizeof(T))); } template void integer_check_sign(const char *type_name, T x, bool expected_signed) { x = 0; --x; if(x > 0 && expected_signed) // "x > 0" to avoid compilation warning when T is unsigned throw Ehardware("integer_check_sign", tools_printf(gettext("%s type is not a signed type as expected"), type_name)); if(x < 1 && !expected_signed) // "x < 1" to avoid compilation warning when T is unsigned we compare against 1 throw Ehardware("integer_check_sign", tools_printf(gettext("%s type is not an unsigned type as expected"), type_name)); } template bool is_unsigned_big_endian(const char *type_name, T x) { unsigned int size = sizeof(x); unsigned char *ptr = (unsigned char *)(& x); unsigned int i = 0; // given type should be unsigned try { integer_check_sign(type_name, x, false); } catch(Ehardware & e) { throw SRC_BUG; } // setting x to the following serie of bytes values: "1|2|3|..|size" x = 0; i = 1; while(i <= size) { x *= 256; x += i%256; ++i; } // looking for a litte endian sequence i = 0; while(i < size && ptr[i] == (size - i)%256) ++i; if(i == size) // full litte endian sequence found return false; if(i > size) throw SRC_BUG; // looking for a big endian sequence i = 0; while(i < size && ptr[i] == (i+1)%256) ++i; if(i == size) // full big endian sequence found return true; else if(i > size) throw SRC_BUG; else // i < size, thus CPU/system uses neither a little nor a big endian sequence!!! throw Ehardware("is_unsigned_big_endian", tools_printf(gettext("type %s is neither big nor little endian! Do not know how to handle integer in a portable manner on this host, aborting"), type_name)); } void integer_check() { U_8 u8 = 0; U_16 u16 = 0; U_32 u32 = 0; U_64 u64 = 0; U_I ui = 0; S_8 s8 = 0; S_16 s16 = 0; S_32 s32 = 0; S_64 s64 = 0; S_I si = 0; // checking integer type width integer_check_width("U_8", u8, 1); integer_check_width("U_16", u16, 2); integer_check_width("U_32", u32, 4); integer_check_width("U_64", u64, 8); integer_check_width("S_8", s8, 1); integer_check_width("S_16", s16, 2); integer_check_width("S_32", s32, 4); integer_check_width("S_64", s64, 8); // checking signed types and unsigned types is as expected integer_check_sign("U_8", u8, false); integer_check_sign("U_16", u16, false); integer_check_sign("U_32", u32, false); integer_check_sign("U_64", u64, false); integer_check_sign("U_I", ui, false); integer_check_sign("S_8", s8, true); integer_check_sign("S_16", s16, true); integer_check_sign("S_32", s32, true); integer_check_sign("S_64", s64, true); integer_check_sign("S_I", si, true); } bool integers_system_is_big_endian() { U_16 u16 = 0; U_32 u32 = 0; U_64 u64 = 0; U_I ui = 0; bool ref; integer_check(); ref = is_unsigned_big_endian("U_16", u16); if(ref != is_unsigned_big_endian("U_32", u32)) throw Ehardware("integers_system_is_big_endian", gettext("incoherent endian between U_16 and U_32")); if(ref != is_unsigned_big_endian("U_64", u64)) throw Ehardware("integers_system_is_big_endian", gettext("incoherent endian between U_16 and U_64")); if(ref != is_unsigned_big_endian("U_I", ui)) throw Ehardware("integers_system_is_big_endian", gettext("incoherent endian between U_16 and U_I")); return ref; } } dar-2.7.15/src/libdar/zstd_module.hpp0000644000175000017500000000454714636066467014417 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file zstd_module.hpp /// \brief per block encryption using zstd algorithm/library /// \ingroup Private /// #ifndef ZSTD_MODULE_HPP #define ZSTD_MODULE_HPP extern "C" { } #include "../my_config.h" #include "compress_module.hpp" #include "infinint.hpp" namespace libdar { /// \addtogroup Private /// @{ class zstd_module: public compress_module { public: zstd_module(U_I compression_level = 9); zstd_module(const zstd_module & ref) = default; zstd_module(zstd_module && ref) noexcept = default; zstd_module & operator = (const zstd_module & ref) = default; zstd_module & operator = (zstd_module && ref) noexcept = default; virtual ~zstd_module() noexcept = default; // inherited from compress_module interface virtual compression get_algo() const override { return compression::zstd; }; virtual U_I get_max_compressing_size() const override; virtual U_I get_min_size_to_compress(U_I clear_size) const override; virtual U_I compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const override; virtual U_I uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const override; virtual std::unique_ptr clone() const override; private: U_I level; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/catalogue.cpp0000644000175000017500000010742414636067146014016 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #ifdef STDC_HEADERS #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" #include #include #include #include "catalogue.hpp" #include "tools.hpp" #include "tronc.hpp" #include "user_interaction.hpp" #include "deci.hpp" #include "header.hpp" #include "defile.hpp" #include "pile.hpp" #include "sparse_file.hpp" #include "fichier_local.hpp" #include "macro_tools.hpp" #include "null_file.hpp" #include "range.hpp" #include "cat_all_entrees.hpp" #include "cat_signature.hpp" using namespace std; namespace libdar { catalogue::catalogue(const std::shared_ptr & ui, const datetime & root_last_modif, const label & data_name): mem_ui(ui), out_compare("/"), in_place(".") // is absolute path when set { contenu = nullptr; try { contenu = new (nothrow) cat_directory(0,0,0,datetime(0),root_last_modif,datetime(0),"root",0); if(contenu == nullptr) throw Ememory("catalogue::catalogue(path)"); current_compare = contenu; current_add = contenu; current_read = contenu; sub_tree = nullptr; ref_data_name = data_name; } catch(...) { if(contenu != nullptr) delete contenu; throw; } stats.clear(); } catalogue::catalogue(const std::shared_ptr & ui, const pile_descriptor &pdesc, const archive_version & reading_ver, compression default_algo, bool lax, const label & lax_layer1_data_name, bool only_detruit): mem_ui(ui), out_compare("/"), in_place(".") { string tmp; saved_status st; unsigned char base; map corres; crc *calc_crc = nullptr; crc *read_crc = nullptr; contenu = nullptr; pdesc.check(false); try { pdesc.stack->reset_crc(CAT_CRC_SIZE); try { if(reading_ver > 7) { // we first need to read the ref_data_name try { ref_data_name.read(*pdesc.stack); } catch(Erange & e) { throw Erange("catalogue::catalogue(generic_file &)", gettext("incoherent catalogue structure")); } } else ref_data_name.clear(); // a cleared data_name is emulated for older archives if(reading_ver >= archive_version(11,1)) { string tmp; try { tools_read_string(*pdesc.stack, tmp); in_place = path(tmp); } catch(Erange & e) { throw Erange("catalogue::catalogue(generic_file &)", gettext("incoherent catalogue structure")); } if(in_place.is_relative() && tmp != ".") throw Erange("catalogue::catalogue(generic_file &)", gettext("incoherent catalogue structure")); } else in_place = path("."); if(lax) { if(ref_data_name != lax_layer1_data_name && !lax_layer1_data_name.is_cleared()) { get_ui().message(gettext("LAX MODE: catalogue label does not match archive label, as if it was an extracted catalogue, assuming data corruption occurred and fixing the catalogue to be considered an a plain internal catalogue")); ref_data_name = lax_layer1_data_name; } } cat_signature cat_sig(*pdesc.stack, reading_ver); if(!cat_sig.get_base_and_status(base, st) && !lax) throw Erange("catalogue::catalogue(generic_file &)", gettext("incoherent catalogue structure")); if(base != 'd' && !lax) throw Erange("catalogue::catalogue(generic_file &)", gettext("incoherent catalogue structure")); stats.clear(); smart_pointer spdesc(new (nothrow) pile_descriptor(pdesc)); if(spdesc.is_null()) throw Ememory("catalogue::catalogue"); contenu = new (nothrow) cat_directory(ui, spdesc, reading_ver, st, stats, corres, default_algo, lax, only_detruit, false); if(contenu == nullptr) throw Ememory("catalogue::catalogue(path)"); if(only_detruit) contenu->remove_all_mirages_and_reduce_dirs(); current_compare = contenu; current_add = contenu; current_read = contenu; sub_tree = nullptr; } catch(...) { calc_crc = pdesc.stack->get_crc(); // keeping "f" in coherent status if(calc_crc != nullptr) { delete calc_crc; calc_crc = nullptr; } throw; } calc_crc = pdesc.stack->get_crc(); // keeping "f" incoherent status in any case if(calc_crc == nullptr) throw SRC_BUG; if(reading_ver > 7) { bool force_crc_failure = false; try { read_crc = create_crc_from_file(*pdesc.stack); } catch(Egeneric & e) { force_crc_failure = true; } if(force_crc_failure || read_crc == nullptr || calc_crc == nullptr || read_crc->get_size() != calc_crc->get_size() || *read_crc != *calc_crc) { if(!lax) throw Erange("catalogue::catalogue(generic_file &)", gettext("CRC failed for the catalogue")); else get_ui().pause(gettext("LAX MODE: CRC failed for catalogue, the archive contents is corrupted. This may even lead dar to see files in the archive that never existed, but this will most probably lead to other failures in restoring files. Shall we proceed anyway?")); } } } catch(...) { if(contenu != nullptr) delete contenu; if(calc_crc != nullptr) delete calc_crc; if(read_crc != nullptr) delete read_crc; throw; } // "contenu" must not be destroyed under normal terminaison! if(calc_crc != nullptr) delete calc_crc; if(read_crc != nullptr) delete read_crc; } catalogue & catalogue::operator = (const catalogue & ref) { detruire(); // now copying the catalogue's data out_compare = ref.out_compare; in_place = ref.in_place; partial_copy_from(ref); return *this; } void catalogue::reset_read() const { current_read = contenu; contenu->reset_read_children(); } void catalogue::end_read() const { current_read = contenu; contenu->end_read(); } void catalogue::skip_read_to_parent_dir() const { cat_directory *tmp = current_read->get_parent(); if(tmp == nullptr) throw Erange("catalogue::skip_read_to_parent_dir", gettext("root does not have a parent directory")); current_read = tmp; } bool catalogue::read(const cat_entree * & ref) const { const cat_nomme *tmp; if(current_read->read_children(tmp)) { const cat_directory *dir = dynamic_cast(tmp); if(dir != nullptr) { current_read = const_cast(dir); dir->reset_read_children(); } ref = tmp; return true; } else { cat_directory *papa = current_read->get_parent(); ref = &r_eod; if(papa == nullptr) return false; // we reached end of root, no cat_eod generation else { current_read = papa; return true; } } } bool catalogue::read_if_present(string *name, const cat_nomme * & ref) const { const cat_nomme *tmp; if(current_read == nullptr) throw Erange("catalogue::read_if_present", gettext("no current directory defined")); if(name == nullptr) // we have to go to parent directory { if(current_read->get_parent() == nullptr) throw Erange("catalogue::read_if_present", gettext("root directory has no parent directory")); else current_read = current_read->get_parent(); ref = nullptr; return true; } else // looking for a real filename if(current_read->search_children(*name, tmp)) { cat_directory *d = dynamic_cast(const_cast(tmp)); if(d != nullptr) // this is a directory need to chdir to it current_read = d; ref = tmp; return true; } else // filename not present in current dir return false; } void catalogue::remove_read_entry(std::string & name) { if(current_read == nullptr) throw Erange("catalogue::remove_read_entry", gettext("no current reading directory defined")); current_read->remove(name); } void catalogue::tail_catalogue_to_current_read() { while(current_read != nullptr) { current_read->tail_to_read_children(); current_read = current_read->get_parent(); } current_read = contenu; } void catalogue::reset_sub_read(const path &sub) { if(! sub.is_relative()) throw SRC_BUG; if(sub_tree != nullptr) delete sub_tree; sub_tree = new (nothrow) path(sub); if(sub_tree == nullptr) throw Ememory("catalogue::reset_sub_read"); sub_count = -1; // must provide the path to subtree; reset_read(); } bool catalogue::sub_read(user_interaction & ui, const cat_entree * &ref) { string tmp; if(sub_tree == nullptr) throw SRC_BUG; // reset_sub_read switch(sub_count) { case 0 : // sending oed to go back to the root if(sub_tree->pop(tmp)) { ref = &r_eod; return true; } else { ref = nullptr; delete sub_tree; sub_tree = nullptr; sub_count = -2; return false; } case -2: // reading is finished return false; case -1: // providing path to sub_tree if(sub_tree->read_subdir(tmp)) { const cat_nomme *xtmp; if(current_read->search_children(tmp, xtmp)) { ref = xtmp; const cat_directory *dir = dynamic_cast(xtmp); if(dir != nullptr) { current_read = const_cast(dir); return true; } else if(sub_tree->read_subdir(tmp)) { ui.message(sub_tree->display() + gettext(" is not present in the archive")); delete sub_tree; sub_tree = nullptr; sub_count = -2; return false; } else // subdir is a single file (no tree)) { sub_count = 0; return true; } } else { ui.message(sub_tree->display() + gettext(" is not present in the archive")); delete sub_tree; sub_tree = nullptr; sub_count = -2; return false; } } else { sub_count = 1; current_read->reset_read_children(); // now reading the sub_tree // no break ! } default: if(read(ref) && sub_count > 0) { const cat_directory *dir = dynamic_cast(ref); const cat_eod *fin = dynamic_cast(ref); if(dir != nullptr) sub_count++; if(fin != nullptr) sub_count--; return true; } else throw SRC_BUG; } } void catalogue::reset_add() { current_add = contenu; } void catalogue::add(cat_entree *ref) { if(current_add == nullptr) throw SRC_BUG; cat_eod *f = dynamic_cast(ref); if(f == nullptr) // ref is not cat_eod { cat_nomme *n = dynamic_cast(ref); cat_directory *t = dynamic_cast(ref); if(n == nullptr) throw SRC_BUG; // unknown type neither "cat_eod" nor "cat_nomme" current_add->add_children(n); if(t != nullptr) // ref is a directory current_add = t; stats.add(ref); } else // ref is an cat_eod { cat_directory *parent = current_add->get_parent(); if(parent == nullptr) throw SRC_BUG; // root has no parent directory, cannot change to it else current_add = parent; delete ref; // all data given throw add becomes owned by the catalogue object } } void catalogue::re_add_in(const string &subdirname) { const cat_nomme *sub = nullptr; if(current_add->search_children(subdirname, sub)) { const cat_directory *subdir = dynamic_cast(sub); if(subdir != nullptr) current_add = const_cast(subdir); else throw Erange("catalogue::re_add_in", gettext("Cannot recurs in a non directory entry")); } else throw Erange("catalogue::re_add_in", gettext("The entry to recurs in does not exist, cannot add further entry to that absent subdirectory")); } void catalogue::re_add_in_replace(const cat_directory &dir) { if(dir.has_children()) throw Erange("catalogue::re_add_in_replace", "Given argument must be an empty dir"); re_add_in(dir.get_name()); *current_add = dir; // the directory's 'operator =' method does preverse existing children of the left (assigned) operand } void catalogue::add_in_current_read(cat_nomme *ref) { if(current_read == nullptr) throw SRC_BUG; // current read directory does not exists current_read->add_children(ref); } void catalogue::reset_compare() const { if(contenu == nullptr) throw SRC_BUG; current_compare = contenu; out_compare = path("/"); } bool catalogue::compare(const cat_entree * target, const cat_entree * & extracted) const { const cat_mirage *mir = dynamic_cast(target); const cat_directory *dir = dynamic_cast(target); const cat_eod *fin = dynamic_cast(target); const cat_nomme *nom = dynamic_cast(target); if(mir != nullptr) dir = dynamic_cast(mir->get_inode()); if(out_compare.degre() > 1) // actually scanning a nonexisting directory { if(dir != nullptr) out_compare += dir->get_name(); else if(fin != nullptr) { string tmp_s; if(!out_compare.pop(tmp_s)) { if(out_compare.is_relative()) throw SRC_BUG; // should not be a relative path !!! else // both cases are bugs, but need to know which case is generating a bug throw SRC_BUG; // out_compare.degre() > 0 but cannot pop ! } } return false; } else // scanning an existing directory { const cat_nomme *found; if(fin != nullptr) { cat_directory *tmp = current_compare->get_parent(); if(tmp == nullptr) throw Erange("catalogue::compare", gettext("root has no parent directory")); current_compare = tmp; extracted = target; return true; } if(nom == nullptr) throw SRC_BUG; // ref, is neither a cat_eod nor a cat_nomme ! what's that ??? if(current_compare->search_children(nom->get_name(), found)) { const cat_detruit *src_det = dynamic_cast(nom); const cat_detruit *dst_det = dynamic_cast(found); const cat_inode *src_ino = dynamic_cast(nom); const cat_inode *dst_ino = dynamic_cast(found); const cat_mirage *src_mir = dynamic_cast(nom); const cat_mirage *dst_mir = dynamic_cast(found); // extracting cat_inode from hard links if(src_mir != nullptr) src_ino = src_mir->get_inode(); if(dst_mir != nullptr) dst_ino = dst_mir->get_inode(); // updating internal structure to follow directory tree : if(dir != nullptr) { const cat_directory *d_ext = dynamic_cast(dst_ino); if(d_ext != nullptr) current_compare = const_cast(d_ext); else out_compare += dir->get_name(); } // now comparing the objects : if(src_ino != nullptr) if(dst_ino != nullptr) { if(!src_ino->same_as(*dst_ino)) return false; } else return false; else if(src_det != nullptr) if(dst_det != nullptr) { if(!dst_det->same_as(*dst_det)) return false; } else return false; else throw SRC_BUG; // src_det == nullptr && src_ino == nullptr, thus a cat_nomme which is neither cat_detruit nor cat_inode ! if(dst_mir != nullptr) extracted = dst_mir->get_inode(); else extracted = found; return true; } else { if(dir != nullptr) out_compare += dir->get_name(); return false; } } } infinint catalogue::update_destroyed_with(const catalogue & ref) { cat_directory *current = contenu; const cat_nomme *ici; const cat_entree *projo; const cat_eod *pro_eod; const cat_directory *pro_dir; const cat_detruit *pro_det; const cat_nomme *pro_nom; const cat_mirage *pro_mir; infinint count = 0; ref.reset_read(); while(ref.read(projo)) { pro_eod = dynamic_cast(projo); pro_dir = dynamic_cast(projo); pro_det = dynamic_cast(projo); pro_nom = dynamic_cast(projo); pro_mir = dynamic_cast(projo); if(pro_eod != nullptr) { cat_directory *tmp = current->get_parent(); if(tmp == nullptr) throw SRC_BUG; // reached root for "contenu", and not yet for "ref"; current = tmp; continue; } if(pro_det != nullptr) continue; if(pro_nom == nullptr) throw SRC_BUG; // neither an cat_eod nor a cat_nomme ! what's that ? if(!current->search_children(pro_nom->get_name(), ici)) { unsigned char firm; if(pro_mir != nullptr) firm = pro_mir->get_inode()->signature(); else firm = pro_nom->signature(); cat_detruit *det_tmp = new (nothrow) cat_detruit(pro_nom->get_name(), firm, current->get_last_modif()); if(det_tmp == nullptr) throw Ememory("catalogue::update_destroyed_with"); try { current->add_children(det_tmp); } catch(...) { delete det_tmp; throw; } count++; if(pro_dir != nullptr) ref.skip_read_to_parent_dir(); } else if(pro_dir != nullptr) { const cat_directory *ici_dir = dynamic_cast(ici); if(ici_dir != nullptr) current = const_cast(ici_dir); else ref.skip_read_to_parent_dir(); } } return count; } void catalogue::update_absent_with(const catalogue & ref, infinint aborting_next_etoile) { cat_directory *current = contenu; const cat_nomme *ici; const cat_entree *projo; const cat_eod *pro_eod; const cat_directory *pro_dir; const cat_detruit *pro_det; const cat_nomme *pro_nom; const cat_inode *pro_ino; const cat_mirage *pro_mir; map corres_clone; // for each etiquette from the reference catalogue // gives an cloned or original cat_etoile object // in the current catalogue ref.reset_read(); while(ref.read(projo)) { pro_eod = dynamic_cast(projo); pro_dir = dynamic_cast(projo); pro_det = dynamic_cast(projo); pro_nom = dynamic_cast(projo); pro_ino = dynamic_cast(projo); pro_mir = dynamic_cast(projo); if(pro_eod != nullptr) { cat_directory *tmp = current->get_parent(); if(tmp == nullptr) throw SRC_BUG; // reached root for "contenu", and not yet for "ref"; current = tmp; continue; } if(pro_det != nullptr) continue; if(pro_nom == nullptr) throw SRC_BUG; // neither an cat_eod nor a cat_nomme! what's that? if(pro_mir != nullptr) pro_ino = pro_mir->get_inode(); // warning: the returned cat_inode's name is undefined // one must use the mirage's own name if(pro_ino == nullptr) throw SRC_BUG; // a nome that is not an cat_inode nor a cat_detruit!? What's that? if(!current->search_children(pro_nom->get_name(), ici)) { cat_entree *clo_ent = nullptr; cat_inode *clo_ino = nullptr; cat_directory *clo_dir = nullptr; cat_mirage *clo_mir = nullptr; cat_etoile *clo_eto = nullptr; try { clo_ent = pro_ino->clone(); clo_ino = dynamic_cast(clo_ent); clo_dir = dynamic_cast(clo_ent); // sanity checks if(clo_ino == nullptr) throw SRC_BUG; // clone of an cat_inode is not an cat_inode??? if((clo_dir != nullptr) ^ (pro_dir != nullptr)) throw SRC_BUG; // both must be nullptr or both must be non nullptr // converting cat_inode to unsaved entry clo_ino->set_saved_status(saved_status::not_saved); if(clo_ino->ea_get_saved_status() != ea_saved_status::none) { if(clo_ino->ea_get_saved_status() == ea_saved_status::removed) clo_ino->ea_set_saved_status(ea_saved_status::none); else clo_ino->ea_set_saved_status(ea_saved_status::partial); } // handling hard links if(pro_mir != nullptr) { try { map::iterator it = corres_clone.find(pro_mir->get_etiquette()); if(it == corres_clone.end()) { clo_eto = new (nothrow) cat_etoile(clo_ino, aborting_next_etoile++); if(clo_eto == nullptr) throw Ememory("catalogue::update_absent_with"); else clo_ent = nullptr; // object now managed by clo_eto try { corres_clone[pro_mir->get_etiquette()] = clo_eto; clo_mir = new (nothrow) cat_mirage(pro_mir->get_name(), clo_eto); if(clo_mir == nullptr) throw Ememory("catalogue::update_absent_with"); } catch(...) { if(clo_eto != nullptr) delete clo_eto; throw; } } else // mapping already exists (a star already shines) { // we have cloned the cat_inode but we do not need it as // an hard linked structure already exists delete clo_ent; clo_ent = nullptr; // so we add a new reference to the existing hard linked structure clo_mir = new (nothrow) cat_mirage(pro_mir->get_name(), it->second); if(clo_mir == nullptr) throw Ememory("catalogue::update_absent_with"); } // adding it to the catalogue current->add_children(clo_mir); } catch(...) { if(clo_mir != nullptr) { delete clo_mir; clo_mir = nullptr; } throw; } } else // not a hard link entry { // adding it to the catalogue current->add_children(clo_ino); clo_ent = nullptr; // object now managed by the current catalogue } // recusing in case of directory if(clo_dir != nullptr) { if(current->search_children(pro_ino->get_name(), ici)) { if((void *)ici != (void *)clo_dir) throw SRC_BUG; // we have just added the entry we were looking for, but could find another one!?! current = clo_dir; } else throw SRC_BUG; // cannot find the entry we have just added!!! } } catch(...) { if(clo_ent != nullptr) { delete clo_ent; clo_ent = nullptr; } throw; } } else // entry found in the current catalogue { if(pro_dir != nullptr) { const cat_directory *ici_dir = dynamic_cast(ici); if(ici_dir != nullptr) current = const_cast(ici_dir); else ref.skip_read_to_parent_dir(); } if(pro_mir != nullptr) { const cat_mirage *ici_mir = dynamic_cast(ici); if(ici_mir != nullptr && corres_clone.find(pro_mir->get_etiquette()) == corres_clone.end()) { // no correspondance found // so we add a one to the map corres_clone[pro_mir->get_etiquette()] = ici_mir->get_etoile(); } } } } } void catalogue::drop_all_non_detruits() { cat_directory *ptr = contenu; const cat_nomme *e = nullptr; const cat_directory *e_dir = nullptr; const cat_detruit *e_det = nullptr; ptr->reset_read_children(); while(ptr != nullptr) { if(ptr->read_children(e)) { e_dir = dynamic_cast(e); e_det = dynamic_cast(e); if(e_dir != nullptr) { ptr = const_cast(e_dir); ptr->reset_read_children(); } else if(e_det == nullptr) ptr->remove(e->get_name()); } else // finished reading the current directory { cat_directory *parent = ptr->get_parent(); if(parent != nullptr && !ptr->has_children()) { parent->remove(ptr->get_name()); ptr = parent; } else ptr = parent; } } } bool catalogue::is_subset_of(const catalogue & ref) const { bool ret = true; const cat_entree *moi = nullptr; const cat_entree *toi = nullptr; reset_read(); ref.reset_compare(); try { while(ret && !read(moi)) { if(moi == nullptr) throw SRC_BUG; if(!ref.compare(moi, toi)) ret = false; else { if(toi == nullptr) throw SRC_BUG; if(*toi != *moi) ret = false; } } } catch(Edata & e) { ret = false; // no rethrow } catch(Erange & e) { ret = false; // no rethrow } return ret; } void catalogue::reset_dump() const { if(contenu == nullptr) throw SRC_BUG; contenu->set_all_mirage_s_inode_dumped_field_to(false); } void catalogue::dump(const pile_descriptor & pdesc) const { crc *tmp = nullptr; pdesc.check(false); if(pdesc.compr->is_compression_suspended()) { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->resume_compression(); } else { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->sync_write(); // required to reset the compression engine and be able to uncompress from that position later on } try { pdesc.stack->reset_crc(CAT_CRC_SIZE); try { ref_data_name.dump(*pdesc.stack); tools_write_string(*pdesc.stack, in_place.display()); contenu->dump(pdesc, false); } catch(...) { tmp = pdesc.stack->get_crc(); throw; } tmp = pdesc.stack->get_crc(); if(tmp == nullptr) throw SRC_BUG; tmp->dump(*pdesc.stack); } catch(...) { if(tmp != nullptr) delete tmp; throw; } if(tmp != nullptr) delete tmp; } void catalogue::reset_all() { out_compare = path("/"); current_compare = contenu; current_add = contenu; current_read = contenu; if(sub_tree != nullptr) { delete sub_tree; sub_tree = nullptr; } } void catalogue::transfer_delta_signatures(const pile_descriptor & destination, bool sequential_read, bool build, const mask & delta_mask, const infinint & delta_sig_min_size, const delta_sig_block_size & signature_block_size) { const cat_entree *ent = nullptr; const cat_file *ent_file = nullptr; const cat_inode *ent_inode = nullptr; const cat_mirage *ent_mir = nullptr; shared_ptr mem(new (nothrow) memory_file()); const crc *my_crc = nullptr; defile juillet = FAKE_ROOT; null_file trash = gf_write_only; generic_file *data = nullptr; U_I block_len; if(!mem) throw Ememory("catalogue::transfer_delta_signature"); if(destination.compr == nullptr || destination.stack == nullptr) throw SRC_BUG; else { destination.stack->sync_write_above(destination.compr); destination.compr->sync_write(); destination.compr->suspend_compression(); } set_all_mirage_s_inode_wrote_field_to(false); reset_read(); while(read(ent)) { ent_file = dynamic_cast(ent); ent_inode = dynamic_cast(ent); ent_mir = dynamic_cast(ent); juillet.enfile(ent); if(ent_mir != nullptr) { if(!ent_mir->is_inode_wrote()) { ent_inode = ent_mir->get_inode(); ent_file = dynamic_cast(ent_inode); ent_mir->set_inode_wrote(true); } } if(ent_file != nullptr) { cat_file *e_file = const_cast(ent_file); if(e_file == nullptr) throw SRC_BUG; if(sequential_read) { const crc * tmp = nullptr; (void)e_file->get_crc(tmp); } if(ent_file->has_delta_signature_structure()) { // delta signature field found, we may have to either: // - drop the global delta_signature structure // - keep the global delta_signature structure but drop the delta_signature data // - keep the global delta_signature structure and keep the delta_signature data, dumping it to the target archive if(!build // we keep existing delta_signature as is || (delta_mask.is_covered(juillet.get_string()) // or we have to build/transfer delta sig if they match size and mask criteria && e_file->get_size() >= delta_sig_min_size)) { shared_ptr sig_ptr; ent_file->read_delta_signature(sig_ptr, block_len); try { if(sig_ptr) e_file->dump_delta_signature(sig_ptr, block_len, *(destination.compr), false); else e_file->dump_delta_signature(*(destination.compr), false); } catch(...) { ent_file->drop_delta_signature_data(); throw; } ent_file->drop_delta_signature_data(); } else // we need to remove the delta signature, but not the delta signature structure when status is saved_status::delta if(e_file->get_saved_status() == saved_status::delta) { e_file->drop_delta_signature_data(); // no need to drop the signature_structure structure outside the catalogue // only the delta_signature_data will stay outside the catalogue not the associated CRC // they will only be kept inside the (isolated) catalogue } else e_file->clear_delta_signature_structure(); } else // no delta signature found we may have to calculate them { if(build && delta_mask.is_covered(juillet.get_string()) && e_file->get_size() >= delta_sig_min_size) { const crc **checksum = nullptr; block_len = signature_block_size.calculate(e_file->get_size()); if(!e_file->has_crc() && !sequential_read) { // this is an old archive, we will add a data crc on-fly checksum = new (nothrow)(const crc *); if(checksum == nullptr) throw Ememory("catalogue::transfer_delta_signatures"); *checksum = nullptr; } try { switch(e_file->get_saved_status()) { case saved_status::saved: data = e_file->get_data(cat_file::plain, mem, block_len, nullptr, checksum); if(data == nullptr) throw SRC_BUG; try { data->copy_to(trash); } catch(...) { delete data; throw; } delete data; if(checksum != nullptr) { if(*checksum != nullptr) e_file->set_crc(**checksum); else throw SRC_BUG; } if(!e_file->get_crc(my_crc)) throw SRC_BUG; if(my_crc == nullptr) throw SRC_BUG; e_file->will_have_delta_signature_available(); e_file->set_patch_base_crc(*my_crc); e_file->set_patch_result_crc(*my_crc); e_file->dump_delta_signature(mem, block_len, *(destination.compr), false); e_file->drop_delta_signature_data(); // now the data has been written to archive we can free up memory break; case saved_status::fake: case saved_status::not_saved: break; case saved_status::delta: // reading the crc from the archive in sequential read mode if(sequential_read) e_file->get_crc(my_crc); break; default: throw SRC_BUG; } } catch(...) { if(checksum != nullptr) { if(*checksum != nullptr) delete *checksum; delete checksum; } throw; } if(checksum != nullptr) { if(*checksum != nullptr) delete *checksum; delete checksum; } } // no signature and no need to add some for that entry } } if(ent_inode != nullptr && sequential_read) { // EA if(ent_inode->ea_get_saved_status() == ea_saved_status::full) { (void)ent_inode->get_ea(); ent_inode->ea_detach(); } // FSA if(ent_inode->fsa_get_saved_status() == fsa_saved_status::full) { (void)ent_inode->get_fsa(); ent_inode->fsa_detach(); } } } } void catalogue::drop_delta_signatures() { const cat_entree *ent = nullptr; const cat_file *ent_file = nullptr; const cat_mirage *ent_mir = nullptr; reset_read(); while(read(ent)) { ent_file = dynamic_cast(ent); ent_mir = dynamic_cast(ent); if(ent_mir != nullptr) ent_file = dynamic_cast(ent_mir->get_inode()); if(ent_file != nullptr) { if(ent_file->has_delta_signature_available()) { cat_file *e_file = const_cast(ent_file); e_file->clear_delta_signature_only(); } } } } bool catalogue::get_in_place(path & arg) const { if(in_place.is_absolute()) { arg = in_place; return true; } else return false; } void catalogue::set_in_place(const path & arg) { if(arg.is_relative()) throw SRC_BUG; in_place = arg; } void catalogue::clear_in_place() { in_place = path("."); } void catalogue::change_location(const pile_descriptor & pdesc) { smart_pointer tmp(new (nothrow) pile_descriptor(pdesc)); if(tmp.is_null()) throw Ememory("catalogue::change_location"); contenu->change_location(tmp); } void catalogue::copy_detruits_from(const catalogue & ref) { const cat_entree *ent; ref.reset_read(); reset_add(); while(ref.read(ent)) { const cat_detruit *ent_det = dynamic_cast(ent); const cat_directory *ent_dir = dynamic_cast(ent); const cat_eod *ent_eod = dynamic_cast(ent); if(ent_dir != nullptr) re_add_in(ent_dir->get_name()); if(ent_eod != nullptr) { cat_eod *tmp = new (nothrow) cat_eod(); if(tmp == nullptr) throw Ememory("catalogue::copy_detruits_from"); try { add(tmp); } catch(...) { delete tmp; throw; } } if(ent_det != nullptr) { cat_detruit *cp = new (nothrow) cat_detruit(*ent_det); if(cp == nullptr) throw Ememory("catalogue::copy_detruits_from"); try { add(cp); } catch(...) { delete cp; throw; } } } } void catalogue::swap_stuff(catalogue & ref) { // swapping contenu cat_directory *tmp = contenu; contenu = ref.contenu; ref.contenu = tmp; tmp = nullptr; // swapping stats entree_stats tmp_st = stats; stats = ref.stats; ref.stats = tmp_st; // swapping label label tmp_lab; tmp_lab = ref_data_name; ref_data_name = ref.ref_data_name; ref.ref_data_name = tmp_lab; // avoid pointers to point to the now other's object tree reset_all(); ref.reset_all(); } void catalogue::partial_copy_from(const catalogue & ref) { contenu = nullptr; sub_tree = nullptr; try { if(ref.contenu == nullptr) throw SRC_BUG; contenu = new (nothrow) cat_directory(*ref.contenu); if(contenu == nullptr) throw Ememory("catalogue::catalogue(const catalogue &)"); current_compare = contenu; current_add = contenu; current_read = contenu; if(ref.sub_tree != nullptr) { sub_tree = new (nothrow) path(*ref.sub_tree); if(sub_tree == nullptr) throw Ememory("catalogue::partial_copy_from"); } else sub_tree = nullptr; sub_count = ref.sub_count; stats = ref.stats; ref_data_name = ref.ref_data_name; } catch(...) { if(contenu != nullptr) { delete contenu; contenu = nullptr; } if(sub_tree != nullptr) { delete sub_tree; sub_tree = nullptr; } throw; } } void catalogue::detruire() { if(contenu != nullptr) { delete contenu; contenu = nullptr; } if(sub_tree != nullptr) { delete sub_tree; sub_tree = nullptr; } } const cat_eod catalogue::r_eod; const U_I catalogue::CAT_CRC_SIZE = 4; } // end of namespace dar-2.7.15/src/libdar/filesystem_backup.hpp0000644000175000017500000000702614636066467015572 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filesystem_backup.hpp /// \brief filesystem_backup class realizes the interface with the filesystem for backing up /// \ingroup Private #ifndef FILESYSTEM_BACKUP_HPP #define FILESYSTEM_BACKUP_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_STAT_H #include #endif } // end extern "C" #include #include "infinint.hpp" #include "etage.hpp" #include "cat_entree.hpp" #include "filesystem_hard_link_read.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// makes a flow sequence of inode to feed the backup filtering routing class filesystem_backup : public filesystem_hard_link_read { public: filesystem_backup(const std::shared_ptr & dialog, const path &root, bool x_info_details, const mask & x_ea_mask, bool check_no_dump_flag, bool alter_atime, bool furtive_read_mode, bool x_cache_directory_tagging, infinint & root_fs_device, bool x_ignore_unknown, const fsa_scope & scope); filesystem_backup(const filesystem_backup & ref) = delete; filesystem_backup(filesystem_backup && ref) = delete; filesystem_backup & operator = (const filesystem_backup & ref) = delete; filesystem_backup & operator = (filesystem_backup && ref) = delete; ~filesystem_backup() { detruire(); }; void reset_read(infinint & root_fs_device); bool read(cat_entree * & ref, infinint & errors, infinint & skipped_dump); void skip_read_to_parent_dir(); // continue reading in parent directory and // ignore all entry not yet read of current directory private: path *fs_root; //< filesystem's root to consider bool info_details; //< detailed information returned to the user mask *ea_mask; //< mask defining the EA to consider bool no_dump_check; //< whether to check against the nodump flag presence bool alter_atime; //< whether to set back atime or not bool furtive_read_mode; //< whether to use furtive read mode (if true, alter_atime is ignored) bool cache_directory_tagging; //< whether to consider cache directory taggin standard path *current_dir; //< needed to translate from an hard linked inode to an already allocated object std::deque pile; //< to store the contents of a directory bool ignore_unknown; //< whether to ignore unknown inode types void detruire(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/crypto_segment.hpp0000644000175000017500000000314414636066467015120 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file crypto_segment.hpp /// \brief defines unit block of information ciphered as once /// \ingroup Private /// #ifndef CRYPTO_SEGMENT_HPP #define CRYPTO_SEGMENT_HPP #include "../my_config.h" #include "mem_block.hpp" namespace libdar { /// \addtogroup Private /// @{ struct crypto_segment { crypto_segment(U_I crypted_size, U_I clear_size): crypted_data(crypted_size), clear_data(clear_size) {}; mem_block crypted_data; mem_block clear_data; infinint block_index; void reset() { crypted_data.reset(); clear_data.reset(); block_index = 0; }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_eod.hpp0000644000175000017500000000431714636066467013457 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_eod.hpp /// \brief object exchanged with a catalogue (never stored in it) to signal the end of a directory /// \ingroup Private #ifndef CAT_EOD_HPP #define CAT_EOD_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_entree.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the End of Directory entry class class cat_eod : public cat_entree { public : cat_eod(): cat_entree(saved_status::saved) {}; cat_eod(const cat_eod & ref) = default; cat_eod(cat_eod && ref) noexcept = default; cat_eod & operator = (const cat_eod & ref) = default; cat_eod & operator = (cat_eod && ref) = default; ~cat_eod() = default; cat_eod(const smart_pointer & pdesc, bool small): cat_entree(pdesc, small, saved_status::saved) {}; // dump defined by cat_entree virtual bool operator == (const cat_entree & ref) const override { return true; }; virtual unsigned char signature() const override { return 'z'; }; virtual std::string get_description() const override { return "end of directory"; }; cat_entree *clone() const override { return new (std::nothrow) cat_eod(); }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive_options_listing_shell.hpp0000644000175000017500000000647714636067146020201 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive_options_listing_shell.hpp /// \brief this file contains shell_interaction options class for listing /// \ingroup API #ifndef ARCHIVE_OPTIONS_LISTING_SHELL_HPP #define ARCHIVE_OPTIONS_LISTING_SHELL_HPP #include "../my_config.h" #include "archive_options.hpp" namespace libdar { /// \addtogroup API /// @{ // /////////////////////////////////////////////////////// // //// OPTIONS FOR LISTING AN ARCHIVE AS IN SHELL /////// // /////////////////////////////////////////////////////// /// class holding optional shell specific parameters used to list the contents of an existing archive /// \note this class is both used for historical reason and user convenience, but to simplify/clarity the API /// the original archive_options_listing only retain the shell independant parameters class archive_options_listing_shell: public archive_options_listing { public: archive_options_listing_shell() { clear(); }; archive_options_listing_shell(const archive_options_listing_shell & ref) = default; archive_options_listing_shell(archive_options_listing_shell && ref) noexcept = default; archive_options_listing_shell & operator = (const archive_options_listing_shell & ref) = default; archive_options_listing_shell & operator = (archive_options_listing_shell && ref) noexcept = default; ~archive_options_listing_shell() = default; virtual void clear() override; /// defines the way archive listing is done: enum listformat { normal, ///< the tar-like listing (this is the default) tree, ///< the original dar's tree listing (for those that like forest) xml, ///< the xml catalogue output slicing ///< the slicing output (give info about where files are located) }; ///////////////////////////////////////////////////////////////////// // setting methods void set_list_mode(listformat list_mode) { x_list_mode = list_mode; set_slicing_location(list_mode == slicing); }; void set_sizes_in_bytes(bool arg) { x_sizes_in_bytes = arg; }; ///////////////////////////////////////////////////////////////////// // getting methods listformat get_list_mode() const { return x_list_mode; }; bool get_sizes_in_bytes() const { return x_sizes_in_bytes; }; private: listformat x_list_mode; bool x_sizes_in_bytes; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/Makefile.in0000644000175000017500000026515014640025156013402 00000000000000# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/libdar ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(dist_noinst_DATA) \ $(noinst_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = libdar.pc.tmpl CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) libdar_la_LIBADD = am__libdar_la_SOURCES_DIST = archive5.cpp archive5.hpp archive_aux.cpp \ archive_aux.hpp archive.cpp archive.hpp \ archive_listing_callback.hpp archive_num.cpp archive_num.hpp \ archive_options5.hpp archive_options.cpp archive_options.hpp \ archive_options_listing_shell.cpp \ archive_options_listing_shell.hpp archive_summary.cpp \ archive_summary.hpp archive_version.cpp archive_version.hpp \ cache.cpp cache_global.cpp cache_global.hpp cache.hpp \ candidates.cpp candidates.hpp capabilities.cpp \ capabilities.hpp cat_all_entrees.hpp catalogue.cpp \ catalogue.hpp cat_blockdev.cpp cat_blockdev.hpp \ cat_chardev.cpp cat_chardev.hpp cat_delta_signature.cpp \ cat_delta_signature.hpp cat_detruit.cpp cat_detruit.hpp \ cat_device.cpp cat_device.hpp cat_directory.cpp \ cat_directory.hpp cat_door.cpp cat_door.hpp cat_entree.cpp \ cat_entree.hpp cat_eod.hpp cat_etoile.cpp cat_etoile.hpp \ cat_file.cpp cat_file.hpp cat_ignored.cpp cat_ignored_dir.cpp \ cat_ignored_dir.hpp cat_ignored.hpp cat_inode.cpp \ cat_inode.hpp cat_lien.cpp cat_lien.hpp cat_mirage.cpp \ cat_mirage.hpp cat_nomme.cpp cat_nomme.hpp cat_prise.cpp \ cat_prise.hpp cat_signature.cpp cat_signature.hpp \ cat_status.hpp cat_tube.cpp cat_tube.hpp \ compile_time_features.cpp compile_time_features.hpp \ compression.cpp compression.hpp compressor.cpp compressor.hpp \ contextual.cpp contextual.hpp crc.cpp crc.hpp crit_action.cpp \ crit_action.hpp criterium.cpp criterium.hpp crypto_asym.cpp \ crypto_asym.hpp crypto.cpp crypto.hpp crypto_sym.cpp \ crypto_sym.hpp cygwin_adapt.hpp cygwin_adapt.h database5.cpp \ database5.hpp database_archives.hpp database_aux.hpp \ database.cpp database_header.cpp database_header.hpp \ database.hpp database_listing_callback.hpp \ database_options.hpp data_dir.cpp data_dir.hpp data_tree.cpp \ data_tree.hpp datetime.cpp datetime.hpp deci.cpp deci.hpp \ defile.cpp defile.hpp ea.cpp ea_filesystem.cpp \ ea_filesystem.hpp ea.hpp elastic.cpp elastic.hpp \ entree_stats.cpp entree_stats.hpp entrepot.cpp entrepot.hpp \ entrepot_libcurl5.hpp entrepot_libcurl.hpp entrepot_local.cpp \ entrepot_local.hpp erreurs.cpp erreurs_ext.cpp erreurs_ext.hpp \ erreurs.hpp escape_catalogue.cpp escape_catalogue.hpp \ escape.cpp escape.hpp etage.cpp etage.hpp fichier_global.cpp \ fichier_global.hpp fichier_local.cpp fichier_local.hpp \ filesystem_backup.cpp filesystem_backup.hpp \ filesystem_diff.cpp filesystem_diff.hpp \ filesystem_hard_link_read.cpp filesystem_hard_link_read.hpp \ filesystem_hard_link_write.cpp filesystem_hard_link_write.hpp \ filesystem_restore.cpp filesystem_restore.hpp \ filesystem_specific_attribute.cpp \ filesystem_specific_attribute.hpp filesystem_tools.cpp \ filesystem_tools.hpp filtre.cpp filtre.hpp fsa_family.cpp \ fsa_family.hpp generic_file.cpp generic_file.hpp \ generic_file_overlay_for_gpgme.cpp \ generic_file_overlay_for_gpgme.hpp generic_rsync.cpp \ generic_rsync.hpp generic_to_global_file.hpp get_version.cpp \ get_version.hpp gf_mode.cpp gf_mode.hpp hash_fichier.cpp \ hash_fichier.hpp header.cpp header.hpp header_version.cpp \ header_version.hpp i_archive.cpp i_archive.hpp i_database.cpp \ i_database.hpp i_entrepot_libcurl.hpp i_libdar_xform.cpp \ i_libdar_xform.hpp infinint.hpp integers.cpp integers.hpp \ int_tools.cpp int_tools.hpp label.cpp label.hpp libdar5.cpp \ libdar5.hpp libdar.hpp libdar_slave.cpp libdar_slave.hpp \ libdar_xform.cpp libdar_xform.hpp limitint.hpp list_entry.cpp \ list_entry.hpp macro_tools.cpp macro_tools.hpp mask.cpp \ mask.hpp mask_list.cpp mask_list.hpp memory_file.cpp \ memory_file.hpp mem_ui.cpp mem_ui.hpp \ mycurl_easyhandle_node.cpp mycurl_easyhandle_node.hpp \ mycurl_easyhandle_sharing.cpp mycurl_easyhandle_sharing.hpp \ mycurl_protocol.cpp mycurl_protocol.hpp nls_swap.hpp \ null_file.hpp op_tools.cpp op_tools.hpp path.cpp path.hpp \ pile.cpp pile_descriptor.cpp pile_descriptor.hpp pile.hpp \ proto_generic_file.hpp range.cpp range.hpp real_infinint.hpp \ sar.cpp sar.hpp sar_tools.cpp sar_tools.hpp scrambler.cpp \ scrambler.hpp secu_memory_file.cpp secu_memory_file.hpp \ secu_string.cpp secu_string.hpp semaphore.cpp semaphore.hpp \ shell_interaction.cpp shell_interaction_emulator.cpp \ shell_interaction_emulator.hpp shell_interaction.hpp \ slave_zapette.cpp slave_zapette.hpp slice_layout.cpp \ slice_layout.hpp smart_pointer.hpp sparse_file.cpp \ sparse_file.hpp statistics.cpp statistics.hpp storage.cpp \ storage.hpp terminateur.cpp terminateur.hpp \ thread_cancellation.cpp thread_cancellation.hpp tlv.cpp \ tlv.hpp tlv_list.cpp tlv_list.hpp tools.cpp tools.hpp \ trivial_sar.cpp trivial_sar.hpp tronc.cpp tronc.hpp \ tronconneuse.cpp tronconneuse.hpp trontextual.cpp \ trontextual.hpp tuyau.cpp tuyau.hpp user_group_bases.cpp \ user_group_bases.hpp user_interaction5.cpp \ user_interaction5.hpp user_interaction_blind.cpp \ user_interaction_blind.hpp user_interaction_callback5.cpp \ user_interaction_callback5.hpp user_interaction_callback.cpp \ user_interaction_callback.hpp user_interaction.cpp \ user_interaction.hpp wrapperlib.cpp wrapperlib.hpp zapette.cpp \ zapette.hpp zapette_protocol.cpp zapette_protocol.hpp \ entrepot_libcurl.cpp fichier_libcurl.cpp \ i_entrepot_libcurl.cpp delta_sig_block_size.cpp mem_block.hpp \ mem_block.cpp heap.hpp parallel_tronconneuse.hpp \ crypto_module.hpp proto_compressor.hpp \ parallel_block_compressor.hpp compress_module.hpp \ lz4_module.hpp lz4_module.cpp block_compressor.cpp \ block_compressor.hpp gzip_module.hpp gzip_module.cpp \ bzip2_module.hpp bzip2_module.cpp lzo_module.hpp \ lzo_module.cpp zstd_module.hpp zstd_module.cpp xz_module.hpp \ xz_module.cpp compressor_zstd.hpp compressor_zstd.cpp \ compress_block_header.hpp compress_block_header.cpp \ header_flags.hpp header_flags.cpp filesystem_ids.cpp \ filesystem_ids.hpp mycurl_param_list.hpp mycurl_param_list.cpp \ mycurl_slist.hpp mycurl_slist.cpp tuyau_global.hpp \ tuyau_global.cpp real_infinint.cpp parallel_tronconneuse.cpp \ parallel_block_compressor.cpp am__objects_1 = archive5.lo archive_aux.lo archive.lo archive_num.lo \ archive_options.lo archive_options_listing_shell.lo \ archive_summary.lo archive_version.lo cache.lo cache_global.lo \ candidates.lo capabilities.lo catalogue.lo cat_blockdev.lo \ cat_chardev.lo cat_delta_signature.lo cat_detruit.lo \ cat_device.lo cat_directory.lo cat_door.lo cat_entree.lo \ cat_etoile.lo cat_file.lo cat_ignored.lo cat_ignored_dir.lo \ cat_inode.lo cat_lien.lo cat_mirage.lo cat_nomme.lo \ cat_prise.lo cat_signature.lo cat_tube.lo \ compile_time_features.lo compression.lo compressor.lo \ contextual.lo crc.lo crit_action.lo criterium.lo \ crypto_asym.lo crypto.lo crypto_sym.lo database5.lo \ database.lo database_header.lo data_dir.lo data_tree.lo \ datetime.lo deci.lo defile.lo ea.lo ea_filesystem.lo \ elastic.lo entree_stats.lo entrepot.lo entrepot_local.lo \ erreurs.lo erreurs_ext.lo escape_catalogue.lo escape.lo \ etage.lo fichier_global.lo fichier_local.lo \ filesystem_backup.lo filesystem_diff.lo \ filesystem_hard_link_read.lo filesystem_hard_link_write.lo \ filesystem_restore.lo filesystem_specific_attribute.lo \ filesystem_tools.lo filtre.lo fsa_family.lo generic_file.lo \ generic_file_overlay_for_gpgme.lo generic_rsync.lo \ get_version.lo gf_mode.lo hash_fichier.lo header.lo \ header_version.lo i_archive.lo i_database.lo i_libdar_xform.lo \ integers.lo int_tools.lo label.lo libdar5.lo libdar_slave.lo \ libdar_xform.lo list_entry.lo macro_tools.lo mask.lo \ mask_list.lo memory_file.lo mem_ui.lo \ mycurl_easyhandle_node.lo mycurl_easyhandle_sharing.lo \ mycurl_protocol.lo op_tools.lo path.lo pile.lo \ pile_descriptor.lo range.lo sar.lo sar_tools.lo scrambler.lo \ secu_memory_file.lo secu_string.lo semaphore.lo \ shell_interaction.lo shell_interaction_emulator.lo \ slave_zapette.lo slice_layout.lo sparse_file.lo statistics.lo \ storage.lo terminateur.lo thread_cancellation.lo tlv.lo \ tlv_list.lo tools.lo trivial_sar.lo tronc.lo tronconneuse.lo \ trontextual.lo tuyau.lo user_group_bases.lo \ user_interaction5.lo user_interaction_blind.lo \ user_interaction_callback5.lo user_interaction_callback.lo \ user_interaction.lo wrapperlib.lo zapette.lo \ zapette_protocol.lo entrepot_libcurl.lo fichier_libcurl.lo \ i_entrepot_libcurl.lo delta_sig_block_size.lo mem_block.lo \ lz4_module.lo block_compressor.lo gzip_module.lo \ bzip2_module.lo lzo_module.lo zstd_module.lo xz_module.lo \ compressor_zstd.lo compress_block_header.lo header_flags.lo \ filesystem_ids.lo mycurl_param_list.lo mycurl_slist.lo \ tuyau_global.lo @WITH_LIBTHREADAR_TRUE@am__objects_2 = parallel_tronconneuse.lo \ @WITH_LIBTHREADAR_TRUE@ parallel_block_compressor.lo am_libdar_la_OBJECTS = $(am__objects_1) real_infinint.lo \ $(am__objects_2) libdar_la_OBJECTS = $(am_libdar_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libdar_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(libdar_la_LDFLAGS) $(LDFLAGS) -o $@ @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@am_libdar_la_rpath = -rpath \ @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@ $(libdir) libdar32_la_LIBADD = am__libdar32_la_SOURCES_DIST = archive5.cpp archive5.hpp \ archive_aux.cpp archive_aux.hpp archive.cpp archive.hpp \ archive_listing_callback.hpp archive_num.cpp archive_num.hpp \ archive_options5.hpp archive_options.cpp archive_options.hpp \ archive_options_listing_shell.cpp \ archive_options_listing_shell.hpp archive_summary.cpp \ archive_summary.hpp archive_version.cpp archive_version.hpp \ cache.cpp cache_global.cpp cache_global.hpp cache.hpp \ candidates.cpp candidates.hpp capabilities.cpp \ capabilities.hpp cat_all_entrees.hpp catalogue.cpp \ catalogue.hpp cat_blockdev.cpp cat_blockdev.hpp \ cat_chardev.cpp cat_chardev.hpp cat_delta_signature.cpp \ cat_delta_signature.hpp cat_detruit.cpp cat_detruit.hpp \ cat_device.cpp cat_device.hpp cat_directory.cpp \ cat_directory.hpp cat_door.cpp cat_door.hpp cat_entree.cpp \ cat_entree.hpp cat_eod.hpp cat_etoile.cpp cat_etoile.hpp \ cat_file.cpp cat_file.hpp cat_ignored.cpp cat_ignored_dir.cpp \ cat_ignored_dir.hpp cat_ignored.hpp cat_inode.cpp \ cat_inode.hpp cat_lien.cpp cat_lien.hpp cat_mirage.cpp \ cat_mirage.hpp cat_nomme.cpp cat_nomme.hpp cat_prise.cpp \ cat_prise.hpp cat_signature.cpp cat_signature.hpp \ cat_status.hpp cat_tube.cpp cat_tube.hpp \ compile_time_features.cpp compile_time_features.hpp \ compression.cpp compression.hpp compressor.cpp compressor.hpp \ contextual.cpp contextual.hpp crc.cpp crc.hpp crit_action.cpp \ crit_action.hpp criterium.cpp criterium.hpp crypto_asym.cpp \ crypto_asym.hpp crypto.cpp crypto.hpp crypto_sym.cpp \ crypto_sym.hpp cygwin_adapt.hpp cygwin_adapt.h database5.cpp \ database5.hpp database_archives.hpp database_aux.hpp \ database.cpp database_header.cpp database_header.hpp \ database.hpp database_listing_callback.hpp \ database_options.hpp data_dir.cpp data_dir.hpp data_tree.cpp \ data_tree.hpp datetime.cpp datetime.hpp deci.cpp deci.hpp \ defile.cpp defile.hpp ea.cpp ea_filesystem.cpp \ ea_filesystem.hpp ea.hpp elastic.cpp elastic.hpp \ entree_stats.cpp entree_stats.hpp entrepot.cpp entrepot.hpp \ entrepot_libcurl5.hpp entrepot_libcurl.hpp entrepot_local.cpp \ entrepot_local.hpp erreurs.cpp erreurs_ext.cpp erreurs_ext.hpp \ erreurs.hpp escape_catalogue.cpp escape_catalogue.hpp \ escape.cpp escape.hpp etage.cpp etage.hpp fichier_global.cpp \ fichier_global.hpp fichier_local.cpp fichier_local.hpp \ filesystem_backup.cpp filesystem_backup.hpp \ filesystem_diff.cpp filesystem_diff.hpp \ filesystem_hard_link_read.cpp filesystem_hard_link_read.hpp \ filesystem_hard_link_write.cpp filesystem_hard_link_write.hpp \ filesystem_restore.cpp filesystem_restore.hpp \ filesystem_specific_attribute.cpp \ filesystem_specific_attribute.hpp filesystem_tools.cpp \ filesystem_tools.hpp filtre.cpp filtre.hpp fsa_family.cpp \ fsa_family.hpp generic_file.cpp generic_file.hpp \ generic_file_overlay_for_gpgme.cpp \ generic_file_overlay_for_gpgme.hpp generic_rsync.cpp \ generic_rsync.hpp generic_to_global_file.hpp get_version.cpp \ get_version.hpp gf_mode.cpp gf_mode.hpp hash_fichier.cpp \ hash_fichier.hpp header.cpp header.hpp header_version.cpp \ header_version.hpp i_archive.cpp i_archive.hpp i_database.cpp \ i_database.hpp i_entrepot_libcurl.hpp i_libdar_xform.cpp \ i_libdar_xform.hpp infinint.hpp integers.cpp integers.hpp \ int_tools.cpp int_tools.hpp label.cpp label.hpp libdar5.cpp \ libdar5.hpp libdar.hpp libdar_slave.cpp libdar_slave.hpp \ libdar_xform.cpp libdar_xform.hpp limitint.hpp list_entry.cpp \ list_entry.hpp macro_tools.cpp macro_tools.hpp mask.cpp \ mask.hpp mask_list.cpp mask_list.hpp memory_file.cpp \ memory_file.hpp mem_ui.cpp mem_ui.hpp \ mycurl_easyhandle_node.cpp mycurl_easyhandle_node.hpp \ mycurl_easyhandle_sharing.cpp mycurl_easyhandle_sharing.hpp \ mycurl_protocol.cpp mycurl_protocol.hpp nls_swap.hpp \ null_file.hpp op_tools.cpp op_tools.hpp path.cpp path.hpp \ pile.cpp pile_descriptor.cpp pile_descriptor.hpp pile.hpp \ proto_generic_file.hpp range.cpp range.hpp real_infinint.hpp \ sar.cpp sar.hpp sar_tools.cpp sar_tools.hpp scrambler.cpp \ scrambler.hpp secu_memory_file.cpp secu_memory_file.hpp \ secu_string.cpp secu_string.hpp semaphore.cpp semaphore.hpp \ shell_interaction.cpp shell_interaction_emulator.cpp \ shell_interaction_emulator.hpp shell_interaction.hpp \ slave_zapette.cpp slave_zapette.hpp slice_layout.cpp \ slice_layout.hpp smart_pointer.hpp sparse_file.cpp \ sparse_file.hpp statistics.cpp statistics.hpp storage.cpp \ storage.hpp terminateur.cpp terminateur.hpp \ thread_cancellation.cpp thread_cancellation.hpp tlv.cpp \ tlv.hpp tlv_list.cpp tlv_list.hpp tools.cpp tools.hpp \ trivial_sar.cpp trivial_sar.hpp tronc.cpp tronc.hpp \ tronconneuse.cpp tronconneuse.hpp trontextual.cpp \ trontextual.hpp tuyau.cpp tuyau.hpp user_group_bases.cpp \ user_group_bases.hpp user_interaction5.cpp \ user_interaction5.hpp user_interaction_blind.cpp \ user_interaction_blind.hpp user_interaction_callback5.cpp \ user_interaction_callback5.hpp user_interaction_callback.cpp \ user_interaction_callback.hpp user_interaction.cpp \ user_interaction.hpp wrapperlib.cpp wrapperlib.hpp zapette.cpp \ zapette.hpp zapette_protocol.cpp zapette_protocol.hpp \ entrepot_libcurl.cpp fichier_libcurl.cpp \ i_entrepot_libcurl.cpp delta_sig_block_size.cpp mem_block.hpp \ mem_block.cpp heap.hpp parallel_tronconneuse.hpp \ crypto_module.hpp proto_compressor.hpp \ parallel_block_compressor.hpp compress_module.hpp \ lz4_module.hpp lz4_module.cpp block_compressor.cpp \ block_compressor.hpp gzip_module.hpp gzip_module.cpp \ bzip2_module.hpp bzip2_module.cpp lzo_module.hpp \ lzo_module.cpp zstd_module.hpp zstd_module.cpp xz_module.hpp \ xz_module.cpp compressor_zstd.hpp compressor_zstd.cpp \ compress_block_header.hpp compress_block_header.cpp \ header_flags.hpp header_flags.cpp filesystem_ids.cpp \ filesystem_ids.hpp mycurl_param_list.hpp mycurl_param_list.cpp \ mycurl_slist.hpp mycurl_slist.cpp tuyau_global.hpp \ tuyau_global.cpp limitint.cpp parallel_tronconneuse.cpp \ parallel_block_compressor.cpp am_libdar32_la_OBJECTS = $(am__objects_1) limitint.lo $(am__objects_2) libdar32_la_OBJECTS = $(am_libdar32_la_OBJECTS) libdar32_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(libdar32_la_LDFLAGS) $(LDFLAGS) -o $@ @BUILD_MODE32_TRUE@am_libdar32_la_rpath = -rpath $(libdir) libdar64_la_LIBADD = am__libdar64_la_SOURCES_DIST = archive5.cpp archive5.hpp \ archive_aux.cpp archive_aux.hpp archive.cpp archive.hpp \ archive_listing_callback.hpp archive_num.cpp archive_num.hpp \ archive_options5.hpp archive_options.cpp archive_options.hpp \ archive_options_listing_shell.cpp \ archive_options_listing_shell.hpp archive_summary.cpp \ archive_summary.hpp archive_version.cpp archive_version.hpp \ cache.cpp cache_global.cpp cache_global.hpp cache.hpp \ candidates.cpp candidates.hpp capabilities.cpp \ capabilities.hpp cat_all_entrees.hpp catalogue.cpp \ catalogue.hpp cat_blockdev.cpp cat_blockdev.hpp \ cat_chardev.cpp cat_chardev.hpp cat_delta_signature.cpp \ cat_delta_signature.hpp cat_detruit.cpp cat_detruit.hpp \ cat_device.cpp cat_device.hpp cat_directory.cpp \ cat_directory.hpp cat_door.cpp cat_door.hpp cat_entree.cpp \ cat_entree.hpp cat_eod.hpp cat_etoile.cpp cat_etoile.hpp \ cat_file.cpp cat_file.hpp cat_ignored.cpp cat_ignored_dir.cpp \ cat_ignored_dir.hpp cat_ignored.hpp cat_inode.cpp \ cat_inode.hpp cat_lien.cpp cat_lien.hpp cat_mirage.cpp \ cat_mirage.hpp cat_nomme.cpp cat_nomme.hpp cat_prise.cpp \ cat_prise.hpp cat_signature.cpp cat_signature.hpp \ cat_status.hpp cat_tube.cpp cat_tube.hpp \ compile_time_features.cpp compile_time_features.hpp \ compression.cpp compression.hpp compressor.cpp compressor.hpp \ contextual.cpp contextual.hpp crc.cpp crc.hpp crit_action.cpp \ crit_action.hpp criterium.cpp criterium.hpp crypto_asym.cpp \ crypto_asym.hpp crypto.cpp crypto.hpp crypto_sym.cpp \ crypto_sym.hpp cygwin_adapt.hpp cygwin_adapt.h database5.cpp \ database5.hpp database_archives.hpp database_aux.hpp \ database.cpp database_header.cpp database_header.hpp \ database.hpp database_listing_callback.hpp \ database_options.hpp data_dir.cpp data_dir.hpp data_tree.cpp \ data_tree.hpp datetime.cpp datetime.hpp deci.cpp deci.hpp \ defile.cpp defile.hpp ea.cpp ea_filesystem.cpp \ ea_filesystem.hpp ea.hpp elastic.cpp elastic.hpp \ entree_stats.cpp entree_stats.hpp entrepot.cpp entrepot.hpp \ entrepot_libcurl5.hpp entrepot_libcurl.hpp entrepot_local.cpp \ entrepot_local.hpp erreurs.cpp erreurs_ext.cpp erreurs_ext.hpp \ erreurs.hpp escape_catalogue.cpp escape_catalogue.hpp \ escape.cpp escape.hpp etage.cpp etage.hpp fichier_global.cpp \ fichier_global.hpp fichier_local.cpp fichier_local.hpp \ filesystem_backup.cpp filesystem_backup.hpp \ filesystem_diff.cpp filesystem_diff.hpp \ filesystem_hard_link_read.cpp filesystem_hard_link_read.hpp \ filesystem_hard_link_write.cpp filesystem_hard_link_write.hpp \ filesystem_restore.cpp filesystem_restore.hpp \ filesystem_specific_attribute.cpp \ filesystem_specific_attribute.hpp filesystem_tools.cpp \ filesystem_tools.hpp filtre.cpp filtre.hpp fsa_family.cpp \ fsa_family.hpp generic_file.cpp generic_file.hpp \ generic_file_overlay_for_gpgme.cpp \ generic_file_overlay_for_gpgme.hpp generic_rsync.cpp \ generic_rsync.hpp generic_to_global_file.hpp get_version.cpp \ get_version.hpp gf_mode.cpp gf_mode.hpp hash_fichier.cpp \ hash_fichier.hpp header.cpp header.hpp header_version.cpp \ header_version.hpp i_archive.cpp i_archive.hpp i_database.cpp \ i_database.hpp i_entrepot_libcurl.hpp i_libdar_xform.cpp \ i_libdar_xform.hpp infinint.hpp integers.cpp integers.hpp \ int_tools.cpp int_tools.hpp label.cpp label.hpp libdar5.cpp \ libdar5.hpp libdar.hpp libdar_slave.cpp libdar_slave.hpp \ libdar_xform.cpp libdar_xform.hpp limitint.hpp list_entry.cpp \ list_entry.hpp macro_tools.cpp macro_tools.hpp mask.cpp \ mask.hpp mask_list.cpp mask_list.hpp memory_file.cpp \ memory_file.hpp mem_ui.cpp mem_ui.hpp \ mycurl_easyhandle_node.cpp mycurl_easyhandle_node.hpp \ mycurl_easyhandle_sharing.cpp mycurl_easyhandle_sharing.hpp \ mycurl_protocol.cpp mycurl_protocol.hpp nls_swap.hpp \ null_file.hpp op_tools.cpp op_tools.hpp path.cpp path.hpp \ pile.cpp pile_descriptor.cpp pile_descriptor.hpp pile.hpp \ proto_generic_file.hpp range.cpp range.hpp real_infinint.hpp \ sar.cpp sar.hpp sar_tools.cpp sar_tools.hpp scrambler.cpp \ scrambler.hpp secu_memory_file.cpp secu_memory_file.hpp \ secu_string.cpp secu_string.hpp semaphore.cpp semaphore.hpp \ shell_interaction.cpp shell_interaction_emulator.cpp \ shell_interaction_emulator.hpp shell_interaction.hpp \ slave_zapette.cpp slave_zapette.hpp slice_layout.cpp \ slice_layout.hpp smart_pointer.hpp sparse_file.cpp \ sparse_file.hpp statistics.cpp statistics.hpp storage.cpp \ storage.hpp terminateur.cpp terminateur.hpp \ thread_cancellation.cpp thread_cancellation.hpp tlv.cpp \ tlv.hpp tlv_list.cpp tlv_list.hpp tools.cpp tools.hpp \ trivial_sar.cpp trivial_sar.hpp tronc.cpp tronc.hpp \ tronconneuse.cpp tronconneuse.hpp trontextual.cpp \ trontextual.hpp tuyau.cpp tuyau.hpp user_group_bases.cpp \ user_group_bases.hpp user_interaction5.cpp \ user_interaction5.hpp user_interaction_blind.cpp \ user_interaction_blind.hpp user_interaction_callback5.cpp \ user_interaction_callback5.hpp user_interaction_callback.cpp \ user_interaction_callback.hpp user_interaction.cpp \ user_interaction.hpp wrapperlib.cpp wrapperlib.hpp zapette.cpp \ zapette.hpp zapette_protocol.cpp zapette_protocol.hpp \ entrepot_libcurl.cpp fichier_libcurl.cpp \ i_entrepot_libcurl.cpp delta_sig_block_size.cpp mem_block.hpp \ mem_block.cpp heap.hpp parallel_tronconneuse.hpp \ crypto_module.hpp proto_compressor.hpp \ parallel_block_compressor.hpp compress_module.hpp \ lz4_module.hpp lz4_module.cpp block_compressor.cpp \ block_compressor.hpp gzip_module.hpp gzip_module.cpp \ bzip2_module.hpp bzip2_module.cpp lzo_module.hpp \ lzo_module.cpp zstd_module.hpp zstd_module.cpp xz_module.hpp \ xz_module.cpp compressor_zstd.hpp compressor_zstd.cpp \ compress_block_header.hpp compress_block_header.cpp \ header_flags.hpp header_flags.cpp filesystem_ids.cpp \ filesystem_ids.hpp mycurl_param_list.hpp mycurl_param_list.cpp \ mycurl_slist.hpp mycurl_slist.cpp tuyau_global.hpp \ tuyau_global.cpp limitint.cpp parallel_tronconneuse.cpp \ parallel_block_compressor.cpp am_libdar64_la_OBJECTS = $(am__objects_1) limitint.lo $(am__objects_2) libdar64_la_OBJECTS = $(am_libdar64_la_OBJECTS) libdar64_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(libdar64_la_LDFLAGS) $(LDFLAGS) -o $@ @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@am_libdar64_la_rpath = -rpath \ @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@ $(libdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/archive.Plo ./$(DEPDIR)/archive5.Plo \ ./$(DEPDIR)/archive_aux.Plo ./$(DEPDIR)/archive_num.Plo \ ./$(DEPDIR)/archive_options.Plo \ ./$(DEPDIR)/archive_options_listing_shell.Plo \ ./$(DEPDIR)/archive_summary.Plo \ ./$(DEPDIR)/archive_version.Plo \ ./$(DEPDIR)/block_compressor.Plo ./$(DEPDIR)/bzip2_module.Plo \ ./$(DEPDIR)/cache.Plo ./$(DEPDIR)/cache_global.Plo \ ./$(DEPDIR)/candidates.Plo ./$(DEPDIR)/capabilities.Plo \ ./$(DEPDIR)/cat_blockdev.Plo ./$(DEPDIR)/cat_chardev.Plo \ ./$(DEPDIR)/cat_delta_signature.Plo \ ./$(DEPDIR)/cat_detruit.Plo ./$(DEPDIR)/cat_device.Plo \ ./$(DEPDIR)/cat_directory.Plo ./$(DEPDIR)/cat_door.Plo \ ./$(DEPDIR)/cat_entree.Plo ./$(DEPDIR)/cat_etoile.Plo \ ./$(DEPDIR)/cat_file.Plo ./$(DEPDIR)/cat_ignored.Plo \ ./$(DEPDIR)/cat_ignored_dir.Plo ./$(DEPDIR)/cat_inode.Plo \ ./$(DEPDIR)/cat_lien.Plo ./$(DEPDIR)/cat_mirage.Plo \ ./$(DEPDIR)/cat_nomme.Plo ./$(DEPDIR)/cat_prise.Plo \ ./$(DEPDIR)/cat_signature.Plo ./$(DEPDIR)/cat_tube.Plo \ ./$(DEPDIR)/catalogue.Plo \ ./$(DEPDIR)/compile_time_features.Plo \ ./$(DEPDIR)/compress_block_header.Plo \ ./$(DEPDIR)/compression.Plo ./$(DEPDIR)/compressor.Plo \ ./$(DEPDIR)/compressor_zstd.Plo ./$(DEPDIR)/contextual.Plo \ ./$(DEPDIR)/crc.Plo ./$(DEPDIR)/crit_action.Plo \ ./$(DEPDIR)/criterium.Plo ./$(DEPDIR)/crypto.Plo \ ./$(DEPDIR)/crypto_asym.Plo ./$(DEPDIR)/crypto_sym.Plo \ ./$(DEPDIR)/data_dir.Plo ./$(DEPDIR)/data_tree.Plo \ ./$(DEPDIR)/database.Plo ./$(DEPDIR)/database5.Plo \ ./$(DEPDIR)/database_header.Plo ./$(DEPDIR)/datetime.Plo \ ./$(DEPDIR)/deci.Plo ./$(DEPDIR)/defile.Plo \ ./$(DEPDIR)/delta_sig_block_size.Plo ./$(DEPDIR)/ea.Plo \ ./$(DEPDIR)/ea_filesystem.Plo ./$(DEPDIR)/elastic.Plo \ ./$(DEPDIR)/entree_stats.Plo ./$(DEPDIR)/entrepot.Plo \ ./$(DEPDIR)/entrepot_libcurl.Plo \ ./$(DEPDIR)/entrepot_local.Plo ./$(DEPDIR)/erreurs.Plo \ ./$(DEPDIR)/erreurs_ext.Plo ./$(DEPDIR)/escape.Plo \ ./$(DEPDIR)/escape_catalogue.Plo ./$(DEPDIR)/etage.Plo \ ./$(DEPDIR)/fichier_global.Plo ./$(DEPDIR)/fichier_libcurl.Plo \ ./$(DEPDIR)/fichier_local.Plo \ ./$(DEPDIR)/filesystem_backup.Plo \ ./$(DEPDIR)/filesystem_diff.Plo \ ./$(DEPDIR)/filesystem_hard_link_read.Plo \ ./$(DEPDIR)/filesystem_hard_link_write.Plo \ ./$(DEPDIR)/filesystem_ids.Plo \ ./$(DEPDIR)/filesystem_restore.Plo \ ./$(DEPDIR)/filesystem_specific_attribute.Plo \ ./$(DEPDIR)/filesystem_tools.Plo ./$(DEPDIR)/filtre.Plo \ ./$(DEPDIR)/fsa_family.Plo ./$(DEPDIR)/generic_file.Plo \ ./$(DEPDIR)/generic_file_overlay_for_gpgme.Plo \ ./$(DEPDIR)/generic_rsync.Plo ./$(DEPDIR)/get_version.Plo \ ./$(DEPDIR)/gf_mode.Plo ./$(DEPDIR)/gzip_module.Plo \ ./$(DEPDIR)/hash_fichier.Plo ./$(DEPDIR)/header.Plo \ ./$(DEPDIR)/header_flags.Plo ./$(DEPDIR)/header_version.Plo \ ./$(DEPDIR)/i_archive.Plo ./$(DEPDIR)/i_database.Plo \ ./$(DEPDIR)/i_entrepot_libcurl.Plo \ ./$(DEPDIR)/i_libdar_xform.Plo ./$(DEPDIR)/int_tools.Plo \ ./$(DEPDIR)/integers.Plo ./$(DEPDIR)/label.Plo \ ./$(DEPDIR)/libdar5.Plo ./$(DEPDIR)/libdar_slave.Plo \ ./$(DEPDIR)/libdar_xform.Plo ./$(DEPDIR)/limitint.Plo \ ./$(DEPDIR)/list_entry.Plo ./$(DEPDIR)/lz4_module.Plo \ ./$(DEPDIR)/lzo_module.Plo ./$(DEPDIR)/macro_tools.Plo \ ./$(DEPDIR)/mask.Plo ./$(DEPDIR)/mask_list.Plo \ ./$(DEPDIR)/mem_block.Plo ./$(DEPDIR)/mem_ui.Plo \ ./$(DEPDIR)/memory_file.Plo \ ./$(DEPDIR)/mycurl_easyhandle_node.Plo \ ./$(DEPDIR)/mycurl_easyhandle_sharing.Plo \ ./$(DEPDIR)/mycurl_param_list.Plo \ ./$(DEPDIR)/mycurl_protocol.Plo ./$(DEPDIR)/mycurl_slist.Plo \ ./$(DEPDIR)/op_tools.Plo \ ./$(DEPDIR)/parallel_block_compressor.Plo \ ./$(DEPDIR)/parallel_tronconneuse.Plo ./$(DEPDIR)/path.Plo \ ./$(DEPDIR)/pile.Plo ./$(DEPDIR)/pile_descriptor.Plo \ ./$(DEPDIR)/range.Plo ./$(DEPDIR)/real_infinint.Plo \ ./$(DEPDIR)/sar.Plo ./$(DEPDIR)/sar_tools.Plo \ ./$(DEPDIR)/scrambler.Plo ./$(DEPDIR)/secu_memory_file.Plo \ ./$(DEPDIR)/secu_string.Plo ./$(DEPDIR)/semaphore.Plo \ ./$(DEPDIR)/shell_interaction.Plo \ ./$(DEPDIR)/shell_interaction_emulator.Plo \ ./$(DEPDIR)/slave_zapette.Plo ./$(DEPDIR)/slice_layout.Plo \ ./$(DEPDIR)/sparse_file.Plo ./$(DEPDIR)/statistics.Plo \ ./$(DEPDIR)/storage.Plo ./$(DEPDIR)/terminateur.Plo \ ./$(DEPDIR)/thread_cancellation.Plo ./$(DEPDIR)/tlv.Plo \ ./$(DEPDIR)/tlv_list.Plo ./$(DEPDIR)/tools.Plo \ ./$(DEPDIR)/trivial_sar.Plo ./$(DEPDIR)/tronc.Plo \ ./$(DEPDIR)/tronconneuse.Plo ./$(DEPDIR)/trontextual.Plo \ ./$(DEPDIR)/tuyau.Plo ./$(DEPDIR)/tuyau_global.Plo \ ./$(DEPDIR)/user_group_bases.Plo \ ./$(DEPDIR)/user_interaction.Plo \ ./$(DEPDIR)/user_interaction5.Plo \ ./$(DEPDIR)/user_interaction_blind.Plo \ ./$(DEPDIR)/user_interaction_callback.Plo \ ./$(DEPDIR)/user_interaction_callback5.Plo \ ./$(DEPDIR)/wrapperlib.Plo ./$(DEPDIR)/xz_module.Plo \ ./$(DEPDIR)/zapette.Plo ./$(DEPDIR)/zapette_protocol.Plo \ ./$(DEPDIR)/zstd_module.Plo am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdar_la_SOURCES) $(libdar32_la_SOURCES) \ $(libdar64_la_SOURCES) DIST_SOURCES = $(am__libdar_la_SOURCES_DIST) \ $(am__libdar32_la_SOURCES_DIST) \ $(am__libdar64_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac DATA = $(dist_noinst_DATA) HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/libdar.pc.tmpl.in \ $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CXXSTDFLAGS = @CXXSTDFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN_PROG = @DOXYGEN_PROG@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GPGME_CFLAGS = @GPGME_CFLAGS@ GPGME_CONFIG = @GPGME_CONFIG@ GPGME_LIBS = @GPGME_LIBS@ GPGRT_CONFIG = @GPGRT_CONFIG@ GREP = @GREP@ HAS_DOT = @HAS_DOT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBCURL_CFLAGS = @LIBCURL_CFLAGS@ LIBCURL_LIBS = @LIBCURL_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTHREADAR_CFLAGS = @LIBTHREADAR_CFLAGS@ LIBTHREADAR_LIBS = @LIBTHREADAR_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGMERGE = @MSGMERGE@ MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POSUB = @POSUB@ PYEXT = @PYEXT@ PYFLAGS = @PYFLAGS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ UPX_PROG = @UPX_PROG@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dot = @dot@ doxygen = @doxygen@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ groff = @groff@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ tmp = @tmp@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ upx = @upx@ @BSD_SED_FALSE@SED_REGEX = -r @BSD_SED_TRUE@SED_REGEX = -E @PROFILING_FALSE@LD_PROF = @PROFILING_TRUE@LD_PROF = -pg @PROFILING_FALSE@CPP_PROF = @PROFILING_TRUE@CPP_PROF = -pg @WITH_LIBTHREADAR_FALSE@LIBTHREADAR_DEP_MODULES = @WITH_LIBTHREADAR_TRUE@LIBTHREADAR_DEP_MODULES = parallel_tronconneuse.cpp parallel_block_compressor.cpp @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@MYLIB = libdar.la @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@MYLIB = libdar64.la @BUILD_MODE32_TRUE@MYLIB = libdar32.la @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@AM_CPPFLAGS = -DDAR_LOCALEDIR=\"$(localedir)\" $(CPP_PROF) @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@AM_CPPFLAGS = -DLIBDAR_MODE=64 -DDAR_LOCALEDIR=\"$(localedir)\" $(CPP_PROF) @BUILD_MODE32_TRUE@AM_CPPFLAGS = -DLIBDAR_MODE=32 -DDAR_LOCALEDIR=\"$(localedir)\" $(CPP_PROF) @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@VER_SRC = real_infinint.o @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@VER_SRC = limitint.o @BUILD_MODE32_TRUE@VER_SRC = limitint.o @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@LIBDAR_SUFFIX = @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@LIBDAR_SUFFIX = 64 @BUILD_MODE32_TRUE@LIBDAR_SUFFIX = 32 @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@LIBDAR_MODE = @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@LIBDAR_MODE = -DLIBDAR_MODE=64 @BUILD_MODE32_TRUE@LIBDAR_MODE = -DLIBDAR_MODE=32 AM_LDFLAGS = $(LTLIBINTL) $(LD_PROF) lib_LTLIBRARIES = $(MYLIB) LIBDAR_MAJOR = `grep LIBDAR_COMPILE_TIME_MAJOR '$(srcdir)/get_version.hpp' | cut -f 2 -d "=" | cut -f 1 -d ';' | sed -e 's% %%g'` LIBDAR_MEDIUM = `grep LIBDAR_COMPILE_TIME_MEDIUM '$(srcdir)/get_version.hpp' | cut -f 2 -d "=" | cut -f 1 -d ';' | sed -e 's% %%g'` LIBDAR_MEDIUM_000 = `grep LIBDAR_COMPILE_TIME_MEDIUM '$(srcdir)/get_version.hpp' | cut -f 2 -d "=" | cut -f 1 -d ';' | sed $(SED_REGEX) -e 's% %%g' -e 's%([^0-9]|^)([0-9]{1})([^0-9]|$$)%0\2%' -e 's%([^0-9]|^)([0-9]{2})([^0-9]|$$)%0\2%'` LIBDAR_MINOR = `grep LIBDAR_COMPILE_TIME_MINOR '$(srcdir)/get_version.hpp' | cut -f 2 -d "=" | cut -f 1 -d ';' | sed -e 's% %%g'` LIBDAR_LIBTOOL_CURRENT = $(LIBDAR_MAJOR)$(LIBDAR_MEDIUM_000) LIBDAR_LIBTOOL_REVISION = $(LIBDAR_MINOR) LIBDAR_LIBTOOL_AGE = $(LIBDAR_MEDIUM) LIBDAR_VERSION_IN = $(LIBDAR_LIBTOOL_CURRENT):$(LIBDAR_LIBTOOL_REVISION):$(LIBDAR_LIBTOOL_AGE) LIBDAR_VERSION_OUT = $(LIBDAR_MAJOR).$(LIBDAR_MEDIUM).$(LIBDAR_MINOR) # header files required by external applications and that must be installed (make install) dist_noinst_DATA = libdar.hpp archive.hpp database.hpp \ libdar_xform.hpp libdar_slave.hpp erreurs.hpp \ compile_time_features.hpp entrepot_libcurl.hpp get_version.hpp \ archive_options_listing_shell.hpp shell_interaction.hpp \ user_interaction_callback.hpp user_interaction_blind.hpp \ path.hpp statistics.hpp archive_options.hpp list_entry.hpp \ crypto.hpp archive_summary.hpp archive_listing_callback.hpp \ user_interaction.hpp database_options.hpp \ database_archives.hpp archive_num.hpp \ database_listing_callback.hpp infinint.hpp archive_aux.hpp \ integers.hpp entrepot.hpp secu_string.hpp mycurl_protocol.hpp \ deci.hpp mask.hpp mask_list.hpp crit_action.hpp fsa_family.hpp \ compression.hpp real_infinint.hpp datetime.hpp range.hpp \ cat_status.hpp ea.hpp entree_stats.hpp database_aux.hpp \ limitint.hpp gf_mode.hpp criterium.hpp int_tools.hpp \ proto_generic_file.hpp storage.hpp archive5.hpp \ archive_options5.hpp database5.hpp entrepot_libcurl5.hpp \ libdar5.hpp user_interaction5.hpp \ user_interaction_callback5.hpp shell_interaction_emulator.hpp \ memory_file.hpp tlv.hpp tlv_list.hpp fichier_global.hpp \ mem_ui.hpp entrepot_local.hpp etage.hpp data_tree.hpp \ tuyau.hpp tools.hpp compressor.hpp generic_file.hpp crc.hpp \ wrapperlib.hpp thread_cancellation.hpp capabilities.hpp \ fichier_local.hpp delta_sig_block_size.hpp \ proto_compressor.hpp parallel_block_compressor.hpp \ block_compressor.hpp compressor_zstd.hpp filesystem_ids.hpp # header files that are internal to libdar and that must not be installed (make install) noinst_HEADERS = archive_version.hpp cache_global.hpp cache.hpp \ candidates.hpp cat_all_entrees.hpp catalogue.hpp \ cat_blockdev.hpp cat_chardev.hpp cat_delta_signature.hpp \ cat_detruit.hpp cat_device.hpp cat_directory.hpp cat_door.hpp \ cat_entree.hpp cat_eod.hpp cat_etoile.hpp cat_file.hpp \ cat_ignored_dir.hpp cat_ignored.hpp cat_inode.hpp cat_lien.hpp \ cat_mirage.hpp cat_nomme.hpp cat_prise.hpp cat_signature.hpp \ cat_tube.hpp contextual.hpp crypto_asym.hpp crypto_sym.hpp \ cygwin_adapt.hpp cygwin_adapt.h database_header.hpp \ data_dir.hpp defile.hpp ea_filesystem.hpp elastic.hpp \ entrepot_libcurl.hpp erreurs_ext.hpp escape_catalogue.hpp \ escape.hpp fichier_libcurl.hpp filesystem_backup.hpp \ filesystem_diff.hpp filesystem_hard_link_read.hpp \ filesystem_hard_link_write.hpp filesystem_restore.hpp \ filesystem_specific_attribute.hpp filesystem_tools.hpp \ filtre.hpp generic_file_overlay_for_gpgme.hpp \ generic_rsync.hpp generic_to_global_file.hpp hash_fichier.hpp \ header.hpp header_version.hpp i_archive.hpp i_database.hpp \ i_entrepot_libcurl.hpp i_libdar_xform.hpp label.hpp \ macro_tools.hpp mycurl_easyhandle_node.hpp \ mycurl_easyhandle_sharing.hpp nls_swap.hpp null_file.hpp \ op_tools.hpp pile_descriptor.hpp pile.hpp sar.hpp \ sar_tools.hpp scrambler.hpp secu_memory_file.hpp semaphore.hpp \ shell_interaction_emulator.hpp slave_zapette.hpp \ slice_layout.hpp smart_pointer.hpp sparse_file.hpp \ terminateur.hpp trivial_sar.hpp tronc.hpp tronconneuse.hpp \ trontextual.hpp user_group_bases.hpp zapette.hpp \ zapette_protocol.hpp mem_block.hpp parallel_tronconneuse.hpp \ crypto_segment.hpp crypto_module.hpp proto_tronco.hpp \ compress_module.hpp lz4_module.hpp gzip_module.hpp \ bzip2_module.hpp lzo_module.hpp zstd_module.hpp xz_module.hpp \ compress_block_header.hpp header_flags.hpp \ mycurl_param_list.hpp mycurl_slist.hpp tuyau_global.hpp ALL_SOURCES = archive5.cpp archive5.hpp archive_aux.cpp \ archive_aux.hpp archive.cpp archive.hpp \ archive_listing_callback.hpp archive_num.cpp archive_num.hpp \ archive_options5.hpp archive_options.cpp archive_options.hpp \ archive_options_listing_shell.cpp \ archive_options_listing_shell.hpp archive_summary.cpp \ archive_summary.hpp archive_version.cpp archive_version.hpp \ cache.cpp cache_global.cpp cache_global.hpp cache.hpp \ candidates.cpp candidates.hpp capabilities.cpp \ capabilities.hpp cat_all_entrees.hpp catalogue.cpp \ catalogue.hpp cat_blockdev.cpp cat_blockdev.hpp \ cat_chardev.cpp cat_chardev.hpp cat_delta_signature.cpp \ cat_delta_signature.hpp cat_detruit.cpp cat_detruit.hpp \ cat_device.cpp cat_device.hpp cat_directory.cpp \ cat_directory.hpp cat_door.cpp cat_door.hpp cat_entree.cpp \ cat_entree.hpp cat_eod.hpp cat_etoile.cpp cat_etoile.hpp \ cat_file.cpp cat_file.hpp cat_ignored.cpp cat_ignored_dir.cpp \ cat_ignored_dir.hpp cat_ignored.hpp cat_inode.cpp \ cat_inode.hpp cat_lien.cpp cat_lien.hpp cat_mirage.cpp \ cat_mirage.hpp cat_nomme.cpp cat_nomme.hpp cat_prise.cpp \ cat_prise.hpp cat_signature.cpp cat_signature.hpp \ cat_status.hpp cat_tube.cpp cat_tube.hpp \ compile_time_features.cpp compile_time_features.hpp \ compression.cpp compression.hpp compressor.cpp compressor.hpp \ contextual.cpp contextual.hpp crc.cpp crc.hpp crit_action.cpp \ crit_action.hpp criterium.cpp criterium.hpp crypto_asym.cpp \ crypto_asym.hpp crypto.cpp crypto.hpp crypto_sym.cpp \ crypto_sym.hpp cygwin_adapt.hpp cygwin_adapt.h database5.cpp \ database5.hpp database_archives.hpp database_aux.hpp \ database.cpp database_header.cpp database_header.hpp \ database.hpp database_listing_callback.hpp \ database_options.hpp data_dir.cpp data_dir.hpp data_tree.cpp \ data_tree.hpp datetime.cpp datetime.hpp deci.cpp deci.hpp \ defile.cpp defile.hpp ea.cpp ea_filesystem.cpp \ ea_filesystem.hpp ea.hpp elastic.cpp elastic.hpp \ entree_stats.cpp entree_stats.hpp entrepot.cpp entrepot.hpp \ entrepot_libcurl5.hpp entrepot_libcurl.hpp entrepot_local.cpp \ entrepot_local.hpp erreurs.cpp erreurs_ext.cpp erreurs_ext.hpp \ erreurs.hpp escape_catalogue.cpp escape_catalogue.hpp \ escape.cpp escape.hpp etage.cpp etage.hpp fichier_global.cpp \ fichier_global.hpp fichier_local.cpp fichier_local.hpp \ filesystem_backup.cpp filesystem_backup.hpp \ filesystem_diff.cpp filesystem_diff.hpp \ filesystem_hard_link_read.cpp filesystem_hard_link_read.hpp \ filesystem_hard_link_write.cpp filesystem_hard_link_write.hpp \ filesystem_restore.cpp filesystem_restore.hpp \ filesystem_specific_attribute.cpp \ filesystem_specific_attribute.hpp filesystem_tools.cpp \ filesystem_tools.hpp filtre.cpp filtre.hpp fsa_family.cpp \ fsa_family.hpp generic_file.cpp generic_file.hpp \ generic_file_overlay_for_gpgme.cpp \ generic_file_overlay_for_gpgme.hpp generic_rsync.cpp \ generic_rsync.hpp generic_to_global_file.hpp get_version.cpp \ get_version.hpp gf_mode.cpp gf_mode.hpp hash_fichier.cpp \ hash_fichier.hpp header.cpp header.hpp header_version.cpp \ header_version.hpp i_archive.cpp i_archive.hpp i_database.cpp \ i_database.hpp i_entrepot_libcurl.hpp i_libdar_xform.cpp \ i_libdar_xform.hpp infinint.hpp integers.cpp integers.hpp \ int_tools.cpp int_tools.hpp label.cpp label.hpp libdar5.cpp \ libdar5.hpp libdar.hpp libdar_slave.cpp libdar_slave.hpp \ libdar_xform.cpp libdar_xform.hpp limitint.hpp list_entry.cpp \ list_entry.hpp macro_tools.cpp macro_tools.hpp mask.cpp \ mask.hpp mask_list.cpp mask_list.hpp memory_file.cpp \ memory_file.hpp mem_ui.cpp mem_ui.hpp \ mycurl_easyhandle_node.cpp mycurl_easyhandle_node.hpp \ mycurl_easyhandle_sharing.cpp mycurl_easyhandle_sharing.hpp \ mycurl_protocol.cpp mycurl_protocol.hpp nls_swap.hpp \ null_file.hpp op_tools.cpp op_tools.hpp path.cpp path.hpp \ pile.cpp pile_descriptor.cpp pile_descriptor.hpp pile.hpp \ proto_generic_file.hpp range.cpp range.hpp real_infinint.hpp \ sar.cpp sar.hpp sar_tools.cpp sar_tools.hpp scrambler.cpp \ scrambler.hpp secu_memory_file.cpp secu_memory_file.hpp \ secu_string.cpp secu_string.hpp semaphore.cpp semaphore.hpp \ shell_interaction.cpp shell_interaction_emulator.cpp \ shell_interaction_emulator.hpp shell_interaction.hpp \ slave_zapette.cpp slave_zapette.hpp slice_layout.cpp \ slice_layout.hpp smart_pointer.hpp sparse_file.cpp \ sparse_file.hpp statistics.cpp statistics.hpp storage.cpp \ storage.hpp terminateur.cpp terminateur.hpp \ thread_cancellation.cpp thread_cancellation.hpp tlv.cpp \ tlv.hpp tlv_list.cpp tlv_list.hpp tools.cpp tools.hpp \ trivial_sar.cpp trivial_sar.hpp tronc.cpp tronc.hpp \ tronconneuse.cpp tronconneuse.hpp trontextual.cpp \ trontextual.hpp tuyau.cpp tuyau.hpp user_group_bases.cpp \ user_group_bases.hpp user_interaction5.cpp \ user_interaction5.hpp user_interaction_blind.cpp \ user_interaction_blind.hpp user_interaction_callback5.cpp \ user_interaction_callback5.hpp user_interaction_callback.cpp \ user_interaction_callback.hpp user_interaction.cpp \ user_interaction.hpp wrapperlib.cpp wrapperlib.hpp zapette.cpp \ zapette.hpp zapette_protocol.cpp zapette_protocol.hpp \ entrepot_libcurl.cpp fichier_libcurl.cpp \ i_entrepot_libcurl.cpp delta_sig_block_size.cpp mem_block.hpp \ mem_block.cpp heap.hpp parallel_tronconneuse.hpp \ crypto_module.hpp proto_compressor.hpp \ parallel_block_compressor.hpp compress_module.hpp \ lz4_module.hpp lz4_module.cpp block_compressor.cpp \ block_compressor.hpp gzip_module.hpp gzip_module.cpp \ bzip2_module.hpp bzip2_module.cpp lzo_module.hpp \ lzo_module.cpp zstd_module.hpp zstd_module.cpp xz_module.hpp \ xz_module.cpp compressor_zstd.hpp compressor_zstd.cpp \ compress_block_header.hpp compress_block_header.cpp \ header_flags.hpp header_flags.cpp filesystem_ids.cpp \ filesystem_ids.hpp mycurl_param_list.hpp mycurl_param_list.cpp \ mycurl_slist.hpp mycurl_slist.cpp tuyau_global.hpp \ tuyau_global.cpp libdar_la_LDFLAGS = -version-info $(LIBDAR_VERSION_IN) libdar_la_SOURCES = $(ALL_SOURCES) real_infinint.cpp $(LIBTHREADAR_DEP_MODULES) libdar32_la_LDFLAGS = -version-info $(LIBDAR_VERSION_IN) libdar32_la_SOURCES = $(ALL_SOURCES) limitint.cpp $(LIBTHREADAR_DEP_MODULES) libdar64_la_LDFLAGS = -version-info $(LIBDAR_VERSION_IN) libdar64_la_SOURCES = $(ALL_SOURCES) limitint.cpp $(LIBTHREADAR_DEP_MODULES) all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libdar/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/libdar/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): libdar.pc.tmpl: $(top_builddir)/config.status $(srcdir)/libdar.pc.tmpl.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdar.la: $(libdar_la_OBJECTS) $(libdar_la_DEPENDENCIES) $(EXTRA_libdar_la_DEPENDENCIES) $(AM_V_CXXLD)$(libdar_la_LINK) $(am_libdar_la_rpath) $(libdar_la_OBJECTS) $(libdar_la_LIBADD) $(LIBS) libdar32.la: $(libdar32_la_OBJECTS) $(libdar32_la_DEPENDENCIES) $(EXTRA_libdar32_la_DEPENDENCIES) $(AM_V_CXXLD)$(libdar32_la_LINK) $(am_libdar32_la_rpath) $(libdar32_la_OBJECTS) $(libdar32_la_LIBADD) $(LIBS) libdar64.la: $(libdar64_la_OBJECTS) $(libdar64_la_DEPENDENCIES) $(EXTRA_libdar64_la_DEPENDENCIES) $(AM_V_CXXLD)$(libdar64_la_LINK) $(am_libdar64_la_rpath) $(libdar64_la_OBJECTS) $(libdar64_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive5.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive_aux.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive_num.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive_options.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive_options_listing_shell.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive_summary.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive_version.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/block_compressor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bzip2_module.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache_global.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/candidates.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/capabilities.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_blockdev.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_chardev.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_delta_signature.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_detruit.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_device.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_directory.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_door.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_entree.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_etoile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_file.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_ignored.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_ignored_dir.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_inode.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_lien.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_mirage.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_nomme.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_prise.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_signature.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat_tube.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/catalogue.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compile_time_features.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress_block_header.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compression.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compressor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compressor_zstd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/contextual.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crit_action.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/criterium.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_asym.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_sym.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data_dir.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data_tree.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/database.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/database5.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/database_header.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datetime.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deci.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delta_sig_block_size.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ea.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ea_filesystem.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elastic.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entree_stats.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entrepot.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entrepot_libcurl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entrepot_local.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/erreurs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/erreurs_ext.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape_catalogue.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/etage.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fichier_global.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fichier_libcurl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fichier_local.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem_backup.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem_diff.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem_hard_link_read.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem_hard_link_write.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem_ids.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem_restore.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem_specific_attribute.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem_tools.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filtre.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsa_family.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generic_file.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generic_file_overlay_for_gpgme.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generic_rsync.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_version.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gf_mode.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gzip_module.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash_fichier.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/header.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/header_flags.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/header_version.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i_archive.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i_database.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i_entrepot_libcurl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i_libdar_xform.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/int_tools.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/integers.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/label.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdar5.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdar_slave.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdar_xform.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/limitint.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_entry.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lz4_module.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lzo_module.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macro_tools.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mask.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mask_list.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mem_block.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mem_ui.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory_file.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mycurl_easyhandle_node.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mycurl_easyhandle_sharing.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mycurl_param_list.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mycurl_protocol.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mycurl_slist.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/op_tools.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parallel_block_compressor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parallel_tronconneuse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/path.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pile_descriptor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/range.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/real_infinint.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sar.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sar_tools.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scrambler.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/secu_memory_file.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/secu_string.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/semaphore.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shell_interaction.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shell_interaction_emulator.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slave_zapette.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slice_layout.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sparse_file.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statistics.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminateur.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread_cancellation.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tlv.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tlv_list.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tools.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trivial_sar.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tronc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tronconneuse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trontextual.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tuyau.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tuyau_global.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_group_bases.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_interaction.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_interaction5.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_interaction_blind.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_interaction_callback.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_interaction_callback5.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wrapperlib.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xz_module.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zapette.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zapette_protocol.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zstd_module.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) all-local installdirs: for dir in "$(DESTDIR)$(libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool clean-local \ mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/archive.Plo -rm -f ./$(DEPDIR)/archive5.Plo -rm -f ./$(DEPDIR)/archive_aux.Plo -rm -f ./$(DEPDIR)/archive_num.Plo -rm -f ./$(DEPDIR)/archive_options.Plo -rm -f ./$(DEPDIR)/archive_options_listing_shell.Plo -rm -f ./$(DEPDIR)/archive_summary.Plo -rm -f ./$(DEPDIR)/archive_version.Plo -rm -f ./$(DEPDIR)/block_compressor.Plo -rm -f ./$(DEPDIR)/bzip2_module.Plo -rm -f ./$(DEPDIR)/cache.Plo -rm -f ./$(DEPDIR)/cache_global.Plo -rm -f ./$(DEPDIR)/candidates.Plo -rm -f ./$(DEPDIR)/capabilities.Plo -rm -f ./$(DEPDIR)/cat_blockdev.Plo -rm -f ./$(DEPDIR)/cat_chardev.Plo -rm -f ./$(DEPDIR)/cat_delta_signature.Plo -rm -f ./$(DEPDIR)/cat_detruit.Plo -rm -f ./$(DEPDIR)/cat_device.Plo -rm -f ./$(DEPDIR)/cat_directory.Plo -rm -f ./$(DEPDIR)/cat_door.Plo -rm -f ./$(DEPDIR)/cat_entree.Plo -rm -f ./$(DEPDIR)/cat_etoile.Plo -rm -f ./$(DEPDIR)/cat_file.Plo -rm -f ./$(DEPDIR)/cat_ignored.Plo -rm -f ./$(DEPDIR)/cat_ignored_dir.Plo -rm -f ./$(DEPDIR)/cat_inode.Plo -rm -f ./$(DEPDIR)/cat_lien.Plo -rm -f ./$(DEPDIR)/cat_mirage.Plo -rm -f ./$(DEPDIR)/cat_nomme.Plo -rm -f ./$(DEPDIR)/cat_prise.Plo -rm -f ./$(DEPDIR)/cat_signature.Plo -rm -f ./$(DEPDIR)/cat_tube.Plo -rm -f ./$(DEPDIR)/catalogue.Plo -rm -f ./$(DEPDIR)/compile_time_features.Plo -rm -f ./$(DEPDIR)/compress_block_header.Plo -rm -f ./$(DEPDIR)/compression.Plo -rm -f ./$(DEPDIR)/compressor.Plo -rm -f ./$(DEPDIR)/compressor_zstd.Plo -rm -f ./$(DEPDIR)/contextual.Plo -rm -f ./$(DEPDIR)/crc.Plo -rm -f ./$(DEPDIR)/crit_action.Plo -rm -f ./$(DEPDIR)/criterium.Plo -rm -f ./$(DEPDIR)/crypto.Plo -rm -f ./$(DEPDIR)/crypto_asym.Plo -rm -f ./$(DEPDIR)/crypto_sym.Plo -rm -f ./$(DEPDIR)/data_dir.Plo -rm -f ./$(DEPDIR)/data_tree.Plo -rm -f ./$(DEPDIR)/database.Plo -rm -f ./$(DEPDIR)/database5.Plo -rm -f ./$(DEPDIR)/database_header.Plo -rm -f ./$(DEPDIR)/datetime.Plo -rm -f ./$(DEPDIR)/deci.Plo -rm -f ./$(DEPDIR)/defile.Plo -rm -f ./$(DEPDIR)/delta_sig_block_size.Plo -rm -f ./$(DEPDIR)/ea.Plo -rm -f ./$(DEPDIR)/ea_filesystem.Plo -rm -f ./$(DEPDIR)/elastic.Plo -rm -f ./$(DEPDIR)/entree_stats.Plo -rm -f ./$(DEPDIR)/entrepot.Plo -rm -f ./$(DEPDIR)/entrepot_libcurl.Plo -rm -f ./$(DEPDIR)/entrepot_local.Plo -rm -f ./$(DEPDIR)/erreurs.Plo -rm -f ./$(DEPDIR)/erreurs_ext.Plo -rm -f ./$(DEPDIR)/escape.Plo -rm -f ./$(DEPDIR)/escape_catalogue.Plo -rm -f ./$(DEPDIR)/etage.Plo -rm -f ./$(DEPDIR)/fichier_global.Plo -rm -f ./$(DEPDIR)/fichier_libcurl.Plo -rm -f ./$(DEPDIR)/fichier_local.Plo -rm -f ./$(DEPDIR)/filesystem_backup.Plo -rm -f ./$(DEPDIR)/filesystem_diff.Plo -rm -f ./$(DEPDIR)/filesystem_hard_link_read.Plo -rm -f ./$(DEPDIR)/filesystem_hard_link_write.Plo -rm -f ./$(DEPDIR)/filesystem_ids.Plo -rm -f ./$(DEPDIR)/filesystem_restore.Plo -rm -f ./$(DEPDIR)/filesystem_specific_attribute.Plo -rm -f ./$(DEPDIR)/filesystem_tools.Plo -rm -f ./$(DEPDIR)/filtre.Plo -rm -f ./$(DEPDIR)/fsa_family.Plo -rm -f ./$(DEPDIR)/generic_file.Plo -rm -f ./$(DEPDIR)/generic_file_overlay_for_gpgme.Plo -rm -f ./$(DEPDIR)/generic_rsync.Plo -rm -f ./$(DEPDIR)/get_version.Plo -rm -f ./$(DEPDIR)/gf_mode.Plo -rm -f ./$(DEPDIR)/gzip_module.Plo -rm -f ./$(DEPDIR)/hash_fichier.Plo -rm -f ./$(DEPDIR)/header.Plo -rm -f ./$(DEPDIR)/header_flags.Plo -rm -f ./$(DEPDIR)/header_version.Plo -rm -f ./$(DEPDIR)/i_archive.Plo -rm -f ./$(DEPDIR)/i_database.Plo -rm -f ./$(DEPDIR)/i_entrepot_libcurl.Plo -rm -f ./$(DEPDIR)/i_libdar_xform.Plo -rm -f ./$(DEPDIR)/int_tools.Plo -rm -f ./$(DEPDIR)/integers.Plo -rm -f ./$(DEPDIR)/label.Plo -rm -f ./$(DEPDIR)/libdar5.Plo -rm -f ./$(DEPDIR)/libdar_slave.Plo -rm -f ./$(DEPDIR)/libdar_xform.Plo -rm -f ./$(DEPDIR)/limitint.Plo -rm -f ./$(DEPDIR)/list_entry.Plo -rm -f ./$(DEPDIR)/lz4_module.Plo -rm -f ./$(DEPDIR)/lzo_module.Plo -rm -f ./$(DEPDIR)/macro_tools.Plo -rm -f ./$(DEPDIR)/mask.Plo -rm -f ./$(DEPDIR)/mask_list.Plo -rm -f ./$(DEPDIR)/mem_block.Plo -rm -f ./$(DEPDIR)/mem_ui.Plo -rm -f ./$(DEPDIR)/memory_file.Plo -rm -f ./$(DEPDIR)/mycurl_easyhandle_node.Plo -rm -f ./$(DEPDIR)/mycurl_easyhandle_sharing.Plo -rm -f ./$(DEPDIR)/mycurl_param_list.Plo -rm -f ./$(DEPDIR)/mycurl_protocol.Plo -rm -f ./$(DEPDIR)/mycurl_slist.Plo -rm -f ./$(DEPDIR)/op_tools.Plo -rm -f ./$(DEPDIR)/parallel_block_compressor.Plo -rm -f ./$(DEPDIR)/parallel_tronconneuse.Plo -rm -f ./$(DEPDIR)/path.Plo -rm -f ./$(DEPDIR)/pile.Plo -rm -f ./$(DEPDIR)/pile_descriptor.Plo -rm -f ./$(DEPDIR)/range.Plo -rm -f ./$(DEPDIR)/real_infinint.Plo -rm -f ./$(DEPDIR)/sar.Plo -rm -f ./$(DEPDIR)/sar_tools.Plo -rm -f ./$(DEPDIR)/scrambler.Plo -rm -f ./$(DEPDIR)/secu_memory_file.Plo -rm -f ./$(DEPDIR)/secu_string.Plo -rm -f ./$(DEPDIR)/semaphore.Plo -rm -f ./$(DEPDIR)/shell_interaction.Plo -rm -f ./$(DEPDIR)/shell_interaction_emulator.Plo -rm -f ./$(DEPDIR)/slave_zapette.Plo -rm -f ./$(DEPDIR)/slice_layout.Plo -rm -f ./$(DEPDIR)/sparse_file.Plo -rm -f ./$(DEPDIR)/statistics.Plo -rm -f ./$(DEPDIR)/storage.Plo -rm -f ./$(DEPDIR)/terminateur.Plo -rm -f ./$(DEPDIR)/thread_cancellation.Plo -rm -f ./$(DEPDIR)/tlv.Plo -rm -f ./$(DEPDIR)/tlv_list.Plo -rm -f ./$(DEPDIR)/tools.Plo -rm -f ./$(DEPDIR)/trivial_sar.Plo -rm -f ./$(DEPDIR)/tronc.Plo -rm -f ./$(DEPDIR)/tronconneuse.Plo -rm -f ./$(DEPDIR)/trontextual.Plo -rm -f ./$(DEPDIR)/tuyau.Plo -rm -f ./$(DEPDIR)/tuyau_global.Plo -rm -f ./$(DEPDIR)/user_group_bases.Plo -rm -f ./$(DEPDIR)/user_interaction.Plo -rm -f ./$(DEPDIR)/user_interaction5.Plo -rm -f ./$(DEPDIR)/user_interaction_blind.Plo -rm -f ./$(DEPDIR)/user_interaction_callback.Plo -rm -f ./$(DEPDIR)/user_interaction_callback5.Plo -rm -f ./$(DEPDIR)/wrapperlib.Plo -rm -f ./$(DEPDIR)/xz_module.Plo -rm -f ./$(DEPDIR)/zapette.Plo -rm -f ./$(DEPDIR)/zapette_protocol.Plo -rm -f ./$(DEPDIR)/zstd_module.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-data-local install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/archive.Plo -rm -f ./$(DEPDIR)/archive5.Plo -rm -f ./$(DEPDIR)/archive_aux.Plo -rm -f ./$(DEPDIR)/archive_num.Plo -rm -f ./$(DEPDIR)/archive_options.Plo -rm -f ./$(DEPDIR)/archive_options_listing_shell.Plo -rm -f ./$(DEPDIR)/archive_summary.Plo -rm -f ./$(DEPDIR)/archive_version.Plo -rm -f ./$(DEPDIR)/block_compressor.Plo -rm -f ./$(DEPDIR)/bzip2_module.Plo -rm -f ./$(DEPDIR)/cache.Plo -rm -f ./$(DEPDIR)/cache_global.Plo -rm -f ./$(DEPDIR)/candidates.Plo -rm -f ./$(DEPDIR)/capabilities.Plo -rm -f ./$(DEPDIR)/cat_blockdev.Plo -rm -f ./$(DEPDIR)/cat_chardev.Plo -rm -f ./$(DEPDIR)/cat_delta_signature.Plo -rm -f ./$(DEPDIR)/cat_detruit.Plo -rm -f ./$(DEPDIR)/cat_device.Plo -rm -f ./$(DEPDIR)/cat_directory.Plo -rm -f ./$(DEPDIR)/cat_door.Plo -rm -f ./$(DEPDIR)/cat_entree.Plo -rm -f ./$(DEPDIR)/cat_etoile.Plo -rm -f ./$(DEPDIR)/cat_file.Plo -rm -f ./$(DEPDIR)/cat_ignored.Plo -rm -f ./$(DEPDIR)/cat_ignored_dir.Plo -rm -f ./$(DEPDIR)/cat_inode.Plo -rm -f ./$(DEPDIR)/cat_lien.Plo -rm -f ./$(DEPDIR)/cat_mirage.Plo -rm -f ./$(DEPDIR)/cat_nomme.Plo -rm -f ./$(DEPDIR)/cat_prise.Plo -rm -f ./$(DEPDIR)/cat_signature.Plo -rm -f ./$(DEPDIR)/cat_tube.Plo -rm -f ./$(DEPDIR)/catalogue.Plo -rm -f ./$(DEPDIR)/compile_time_features.Plo -rm -f ./$(DEPDIR)/compress_block_header.Plo -rm -f ./$(DEPDIR)/compression.Plo -rm -f ./$(DEPDIR)/compressor.Plo -rm -f ./$(DEPDIR)/compressor_zstd.Plo -rm -f ./$(DEPDIR)/contextual.Plo -rm -f ./$(DEPDIR)/crc.Plo -rm -f ./$(DEPDIR)/crit_action.Plo -rm -f ./$(DEPDIR)/criterium.Plo -rm -f ./$(DEPDIR)/crypto.Plo -rm -f ./$(DEPDIR)/crypto_asym.Plo -rm -f ./$(DEPDIR)/crypto_sym.Plo -rm -f ./$(DEPDIR)/data_dir.Plo -rm -f ./$(DEPDIR)/data_tree.Plo -rm -f ./$(DEPDIR)/database.Plo -rm -f ./$(DEPDIR)/database5.Plo -rm -f ./$(DEPDIR)/database_header.Plo -rm -f ./$(DEPDIR)/datetime.Plo -rm -f ./$(DEPDIR)/deci.Plo -rm -f ./$(DEPDIR)/defile.Plo -rm -f ./$(DEPDIR)/delta_sig_block_size.Plo -rm -f ./$(DEPDIR)/ea.Plo -rm -f ./$(DEPDIR)/ea_filesystem.Plo -rm -f ./$(DEPDIR)/elastic.Plo -rm -f ./$(DEPDIR)/entree_stats.Plo -rm -f ./$(DEPDIR)/entrepot.Plo -rm -f ./$(DEPDIR)/entrepot_libcurl.Plo -rm -f ./$(DEPDIR)/entrepot_local.Plo -rm -f ./$(DEPDIR)/erreurs.Plo -rm -f ./$(DEPDIR)/erreurs_ext.Plo -rm -f ./$(DEPDIR)/escape.Plo -rm -f ./$(DEPDIR)/escape_catalogue.Plo -rm -f ./$(DEPDIR)/etage.Plo -rm -f ./$(DEPDIR)/fichier_global.Plo -rm -f ./$(DEPDIR)/fichier_libcurl.Plo -rm -f ./$(DEPDIR)/fichier_local.Plo -rm -f ./$(DEPDIR)/filesystem_backup.Plo -rm -f ./$(DEPDIR)/filesystem_diff.Plo -rm -f ./$(DEPDIR)/filesystem_hard_link_read.Plo -rm -f ./$(DEPDIR)/filesystem_hard_link_write.Plo -rm -f ./$(DEPDIR)/filesystem_ids.Plo -rm -f ./$(DEPDIR)/filesystem_restore.Plo -rm -f ./$(DEPDIR)/filesystem_specific_attribute.Plo -rm -f ./$(DEPDIR)/filesystem_tools.Plo -rm -f ./$(DEPDIR)/filtre.Plo -rm -f ./$(DEPDIR)/fsa_family.Plo -rm -f ./$(DEPDIR)/generic_file.Plo -rm -f ./$(DEPDIR)/generic_file_overlay_for_gpgme.Plo -rm -f ./$(DEPDIR)/generic_rsync.Plo -rm -f ./$(DEPDIR)/get_version.Plo -rm -f ./$(DEPDIR)/gf_mode.Plo -rm -f ./$(DEPDIR)/gzip_module.Plo -rm -f ./$(DEPDIR)/hash_fichier.Plo -rm -f ./$(DEPDIR)/header.Plo -rm -f ./$(DEPDIR)/header_flags.Plo -rm -f ./$(DEPDIR)/header_version.Plo -rm -f ./$(DEPDIR)/i_archive.Plo -rm -f ./$(DEPDIR)/i_database.Plo -rm -f ./$(DEPDIR)/i_entrepot_libcurl.Plo -rm -f ./$(DEPDIR)/i_libdar_xform.Plo -rm -f ./$(DEPDIR)/int_tools.Plo -rm -f ./$(DEPDIR)/integers.Plo -rm -f ./$(DEPDIR)/label.Plo -rm -f ./$(DEPDIR)/libdar5.Plo -rm -f ./$(DEPDIR)/libdar_slave.Plo -rm -f ./$(DEPDIR)/libdar_xform.Plo -rm -f ./$(DEPDIR)/limitint.Plo -rm -f ./$(DEPDIR)/list_entry.Plo -rm -f ./$(DEPDIR)/lz4_module.Plo -rm -f ./$(DEPDIR)/lzo_module.Plo -rm -f ./$(DEPDIR)/macro_tools.Plo -rm -f ./$(DEPDIR)/mask.Plo -rm -f ./$(DEPDIR)/mask_list.Plo -rm -f ./$(DEPDIR)/mem_block.Plo -rm -f ./$(DEPDIR)/mem_ui.Plo -rm -f ./$(DEPDIR)/memory_file.Plo -rm -f ./$(DEPDIR)/mycurl_easyhandle_node.Plo -rm -f ./$(DEPDIR)/mycurl_easyhandle_sharing.Plo -rm -f ./$(DEPDIR)/mycurl_param_list.Plo -rm -f ./$(DEPDIR)/mycurl_protocol.Plo -rm -f ./$(DEPDIR)/mycurl_slist.Plo -rm -f ./$(DEPDIR)/op_tools.Plo -rm -f ./$(DEPDIR)/parallel_block_compressor.Plo -rm -f ./$(DEPDIR)/parallel_tronconneuse.Plo -rm -f ./$(DEPDIR)/path.Plo -rm -f ./$(DEPDIR)/pile.Plo -rm -f ./$(DEPDIR)/pile_descriptor.Plo -rm -f ./$(DEPDIR)/range.Plo -rm -f ./$(DEPDIR)/real_infinint.Plo -rm -f ./$(DEPDIR)/sar.Plo -rm -f ./$(DEPDIR)/sar_tools.Plo -rm -f ./$(DEPDIR)/scrambler.Plo -rm -f ./$(DEPDIR)/secu_memory_file.Plo -rm -f ./$(DEPDIR)/secu_string.Plo -rm -f ./$(DEPDIR)/semaphore.Plo -rm -f ./$(DEPDIR)/shell_interaction.Plo -rm -f ./$(DEPDIR)/shell_interaction_emulator.Plo -rm -f ./$(DEPDIR)/slave_zapette.Plo -rm -f ./$(DEPDIR)/slice_layout.Plo -rm -f ./$(DEPDIR)/sparse_file.Plo -rm -f ./$(DEPDIR)/statistics.Plo -rm -f ./$(DEPDIR)/storage.Plo -rm -f ./$(DEPDIR)/terminateur.Plo -rm -f ./$(DEPDIR)/thread_cancellation.Plo -rm -f ./$(DEPDIR)/tlv.Plo -rm -f ./$(DEPDIR)/tlv_list.Plo -rm -f ./$(DEPDIR)/tools.Plo -rm -f ./$(DEPDIR)/trivial_sar.Plo -rm -f ./$(DEPDIR)/tronc.Plo -rm -f ./$(DEPDIR)/tronconneuse.Plo -rm -f ./$(DEPDIR)/trontextual.Plo -rm -f ./$(DEPDIR)/tuyau.Plo -rm -f ./$(DEPDIR)/tuyau_global.Plo -rm -f ./$(DEPDIR)/user_group_bases.Plo -rm -f ./$(DEPDIR)/user_interaction.Plo -rm -f ./$(DEPDIR)/user_interaction5.Plo -rm -f ./$(DEPDIR)/user_interaction_blind.Plo -rm -f ./$(DEPDIR)/user_interaction_callback.Plo -rm -f ./$(DEPDIR)/user_interaction_callback5.Plo -rm -f ./$(DEPDIR)/wrapperlib.Plo -rm -f ./$(DEPDIR)/xz_module.Plo -rm -f ./$(DEPDIR)/zapette.Plo -rm -f ./$(DEPDIR)/zapette_protocol.Plo -rm -f ./$(DEPDIR)/zstd_module.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLTLIBRARIES uninstall-local .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ check-am clean clean-generic clean-libLTLIBRARIES \ clean-libtool clean-local cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-data-local install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-libLTLIBRARIES \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-libLTLIBRARIES uninstall-local .PRECIOUS: Makefile @PROFILING_TRUE@clean-local: @PROFILING_TRUE@ rm -f gmon.out @PROFILING_TRUE@ rm -f libdar.pc @PROFILING_FALSE@clean-local: @PROFILING_FALSE@ rm -f libdar.pc install-data-local: mkdir -p $(DESTDIR)$(pkgincludedir) cp ../../config.h $(DESTDIR)$(pkgincludedir)/libdar_config.h sed -e 's%config.h%libdar_config.h%g' -e 's%HAVE_CONFIG_H%1%g' -e 's%MY_CONFIG_H%LIBDAR_MY_CONFIG_H%g' '$(srcdir)/../my_config.h' > ./libdar_my_config.h cp '$(srcdir)/../gettext.h' . for file in $(dist_noinst_DATA) gettext.h libdar_my_config.h ; do if [ -e $(srcdir)/$$file ] ; then src=$(srcdir)/$$file ; else src=$$file ; fi ; sed -e 's%#include \"../my_config.h\"%INC_MY_CONFIG_FILE_H%g' -e "s%#include \"%#include \"$(pkgincludedir)/%g" -e "s%INC_MY_CONFIG_FILE_H%#include \"$(pkgincludedir)/libdar_my_config.h\"%g" "$$src" > $(DESTDIR)$(pkgincludedir)/"$$file" ; done rm libdar_my_config.h gettext.h for file in $(DESTDIR)$(pkgincludedir)/* ; do sed -e 's%HAVE_%LIBDAR_HAS_%g' -e 's%PACKAGE%DAR_PACKAGE%g' -e 's%SIZEOF_%LIBDAR_SIZEOF_%g' -e 's%LSTAT_FOLLOWS_SLASHED_SYMLINK%LIBDAR_LSTAT_FOLLOWS_SLASHED_SYMLINK%g' -e 's%VERSION%LIBDAR_VERSION%g' -e 's%MUTEX_WORKS%LIBDAR_MUTEX_WORKS%g' -e 's%OS_BITS%LIBDAR_OS_BITS%g' -e 's%_AVAILABLE%_AVAILABLE_FOR_LIBDAR%g' -e 's%STDC_HEADERS%LIBDAR_STDC_HEADERS%g' -e 's%ENABLE_NLS%DAR_ENABLE_NLS%g' -e 's%HAVE_GETTEXT%DAR_HAS_GETTEXT%g' "$$file" > "$$file.tmp" && mv "$$file.tmp" "$$file" ; done egrep 'LIBDAR|DAR_' $(DESTDIR)$(pkgincludedir)/libdar_config.h | grep -v "#undef" > $(DESTDIR)$(pkgincludedir)/config.h.tmp mv $(DESTDIR)$(pkgincludedir)/config.h.tmp $(DESTDIR)$(pkgincludedir)/libdar_config.h chmod 0644 $(DESTDIR)$(pkgincludedir)/* chmod 0755 $(DESTDIR)$(pkgincludedir) $(INSTALL) -d $(DESTDIR)$(pkgconfigdir) $(INSTALL) -m 0644 libdar.pc $(DESTDIR)$(pkgconfigdir)/libdar$(LIBDAR_SUFFIX).pc uninstall-local: rm -rf $(DESTDIR)$(pkgincludedir) $(DESTDIR)$(pkgconfigdir)/libdar$(LIBDAR_SUFFIX).pc all-local : libdar.pc libdar.pc : libdar.pc.tmpl sed -e "s%#LIBDAR_VERSION#%$(LIBDAR_VERSION_OUT)%g" -e "s%#LIBDAR_SUFFIX#%$(LIBDAR_SUFFIX)%g" -e "s%#LIBDAR_MODE#%$(LIBDAR_MODE)%g" -e "s%#CXXFLAGS#%$(CXXFLAGS)%g" -e "s%#CXXSTDFLAGS#%$(CXXSTDFLAGS)%g" libdar.pc.tmpl > libdar.pc # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dar-2.7.15/src/libdar/mem_ui.cpp0000644000175000017500000000312414636066467013322 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "infinint.hpp" // yep, strange thing to have to include the therorically less dependent header here. // including "mem_ui.hpp" here, lead to cyclic dependancy of headers... this points needs to be clarified #include "mem_ui.hpp" #include "shell_interaction.hpp" using namespace std; namespace libdar { mem_ui::mem_ui(const std::shared_ptr & dialog): ui(dialog) { try { if(!ui) ui = make_shared(cerr, cerr, false); } catch(std::bad_alloc & e) { throw Ememory("mem_ui::mem_ui"); } } } // end of namespace dar-2.7.15/src/libdar/capabilities.cpp0000644000175000017500000001302014636066467014474 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_SYS_CAPABILITY_H #include #else #if HAVE_LINUX_CAPABILITY_H #include #endif #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif } #include "capabilities.hpp" #include "tools.hpp" using namespace std; namespace libdar { #ifndef HAVE_CAPABILITIES capa_status capability_LINUX_IMMUTABLE(user_interaction & ui, bool verbose) { return capa_unknown; } capa_status capability_SYS_RESOURCE(user_interaction & ui, bool verbose) { return capa_unknown; } capa_status capability_FOWNER(user_interaction & ui, bool verbose) { return capa_unknown; } capa_status capability_CHOWN(user_interaction & ui, bool verbose) { return capa_unknown; } #else static capa_status lxcapa_check(cap_value_t capa, cap_flag_t capa_from_set, user_interaction & ui, bool verbose, const std::string & capa_name); static bool lxcapa_set(cap_value_t capa, cap_flag_t capa_from_set, bool value, user_interaction & ui, bool verbose, const std::string & capa_name); capa_status lxcapa_activate(cap_value_t capa, user_interaction & ui, bool verbose, const std::string & capa_name); capa_status capability_LINUX_IMMUTABLE(user_interaction & ui, bool verbose) { return lxcapa_activate(CAP_LINUX_IMMUTABLE, ui, verbose, "Immutable"); } capa_status capability_SYS_RESOURCE(user_interaction & ui, bool verbose) { return lxcapa_activate(CAP_SYS_RESOURCE, ui, verbose, "System Resource"); } capa_status capability_FOWNER(user_interaction & ui, bool verbose) { return lxcapa_activate(CAP_FOWNER, ui, verbose, "File Owner for all files"); } capa_status capability_CHOWN(user_interaction & ui, bool verbose) { return lxcapa_activate(CAP_CHOWN, ui, verbose, "change ownership"); } static capa_status lxcapa_check(cap_value_t capa, cap_flag_t capa_from_set, user_interaction & ui, bool verbose, const std::string & capa_name) { capa_status ret = capa_unknown; cap_t capaset = cap_get_proc(); cap_flag_value_t val; try { if(cap_get_flag(capaset, capa, capa_from_set, &val) == 0) ret = (val == CAP_SET) ? capa_set : capa_clear; else { ret = capa_unknown; if(verbose) { string tmp = tools_strerror_r(errno); ui.printf(gettext("Error met while checking for capability %S: %s"), &capa_name, tmp.c_str()); } } } catch(...) // well a try/catch may seems useless here, but it does not hurt ... :-) { cap_free(capaset); throw; } cap_free(capaset); return ret; } static bool lxcapa_set(cap_value_t capa, cap_flag_t capa_from_set, bool value, user_interaction & ui, bool verbose, const std::string & capa_name) { cap_t capaset = cap_get_proc(); cap_flag_value_t flag_value = value ? CAP_SET : CAP_CLEAR; bool ret = false; try { if(cap_set_flag(capaset, capa_from_set, 1, &capa, flag_value) != 0) { string tmp = tools_strerror_r(errno); ui.printf(gettext("Error met while setting capability %S: %s"), &capa_name, tmp.c_str()); } else // no error so far { if(cap_set_proc(capaset) != 0) { string tmp = tools_strerror_r(errno); ui.printf(gettext("Error met while setting capability %S: %s"), &capa_name, tmp.c_str()); } else // no error so far ret = true; } } catch(...) // well a try/catch may seems useless here, but it does not hurt ... :-) { cap_free(capaset); throw; } cap_free(capaset); return ret; } capa_status lxcapa_activate(cap_value_t capa, user_interaction & ui, bool verbose, const std::string & capa_name) { capa_status ret = lxcapa_check(capa, CAP_EFFECTIVE, ui, verbose, capa_name); if(ret == capa_clear) { // maybe capability is available in CAP_PERMITTED if(lxcapa_check(capa, CAP_PERMITTED, ui, verbose, capa_name) == capa_set) { // yes it is we can try activating the capability in CAP_EFFECTIVE if(lxcapa_set(capa, CAP_EFFECTIVE, true, ui, verbose, capa_name)) { // checking if the status has changed ret = lxcapa_check(capa, CAP_EFFECTIVE, ui, verbose, capa_name); } } // capability not in CAP_PERMITTED, not trying to activating it } return ret; } #endif } // end of namespace dar-2.7.15/src/libdar/memory_file.hpp0000644000175000017500000000575214636066467014374 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file memory_file.hpp /// \brief Memory_file is a generic_file class that only uses virtual memory /// \ingroup Private #ifndef MEMORY_FILE_HPP #define MEMORY_FILE_HPP #include "generic_file.hpp" #include "storage.hpp" namespace libdar { /// \addtogroup Private /// @{ /// generic_file stored in memory class memory_file : public generic_file { public: /// Constructors & Destructor memory_file() : generic_file(gf_read_write), data(0) { position = 0; }; memory_file(const memory_file & ref) = default; memory_file(memory_file && ref) noexcept = default; memory_file & operator = (const memory_file & ref) = default; memory_file & operator = (memory_file && ref) noexcept = default; ~memory_file() = default; // memory_storage specific methods void reset() { if(is_terminated()) throw SRC_BUG; position = 0; data = storage(0); }; infinint size() const { return data.size(); }; // virtual method inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override { return true; }; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return true; }; virtual infinint get_position() const override { if(is_terminated()) throw SRC_BUG; return position; }; protected: // virtual method inherited from generic_file virtual void inherited_read_ahead(const infinint & amount) override {}; // no optimization can be done here, we rely on the OS here virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override {}; private: storage data; infinint position; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/zapette_protocol.hpp0000644000175000017500000000512514636066467015454 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file zapette_protocol.hpp /// \brief protocol management between archive and libdar_slave classes /// \ingroup Private #include "../my_config.h" #ifndef ZAPETTE_PROTOCOL_HPP #define ZAPETTE_PROTOCOL_HPP extern "C" { } // end extern "C" #include #include #include "integers.hpp" #include "infinint.hpp" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ constexpr unsigned char ANSWER_TYPE_DATA = 'D'; constexpr unsigned char ANSWER_TYPE_INFININT = 'I'; constexpr U_I REQUEST_SIZE_SPECIAL_ORDER = 0; constexpr U_I REQUEST_OFFSET_END_TRANSMIT = 0; constexpr U_I REQUEST_OFFSET_GET_FILESIZE = 1; constexpr U_I REQUEST_OFFSET_CHANGE_CONTEXT_STATUS = 2; constexpr U_I REQUEST_IS_OLD_START_END_ARCHIVE = 3; constexpr U_I REQUEST_GET_DATA_NAME = 4; constexpr U_I REQUEST_FIRST_SLICE_HEADER_SIZE = 5; constexpr U_I REQUEST_OTHER_SLICE_HEADER_SIZE = 6; struct request { char serial_num; U_16 size; // size or REQUEST_SIZE_SPECIAL_ORDER infinint offset; // offset or REQUEST_OFFSET_END_TRANSMIT or REQUEST_OFFSET_GET_FILESIZE, REQUEST_OFFSET_* ... std::string info; // new contextual_status void write(generic_file *f); // master side void read(generic_file *f); // slave side }; struct answer { char serial_num; char type; U_16 size; infinint arg; void write(generic_file *f, char *data); // slave side void read(generic_file *f, char *data, U_16 max); // master side }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/lzo_module.hpp0000644000175000017500000000516514636066467014234 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file lzo_module.hpp /// \brief per block encryption using LZO algorithm/library /// \ingroup Private /// #ifndef LZO_MODULE_HPP #define LZO_MODULE_HPP extern "C" { } #include "../my_config.h" #include #include #include "compress_module.hpp" namespace libdar { /// \addtogroup Private /// @{ class lzo_module: public compress_module { public: lzo_module(compression algo, U_I compression_level = 9) { init(algo, compression_level); }; lzo_module(const lzo_module & ref) { init(ref.lzo_algo, ref.level); }; lzo_module(lzo_module && ref) noexcept = default; lzo_module & operator = (const lzo_module & ref) { init(ref.lzo_algo, ref.level); return *this; }; lzo_module & operator = (lzo_module && ref) noexcept = default; virtual ~lzo_module() noexcept = default; // inherited from compress_module interface virtual compression get_algo() const override { return compression::lzo; }; virtual U_I get_max_compressing_size() const override; virtual U_I get_min_size_to_compress(U_I clear_size) const override; virtual U_I compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const override; virtual U_I uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const override; virtual std::unique_ptr clone() const override; private: compression lzo_algo; U_I level; std::unique_ptr wrkmem_decompr; std::unique_ptr wrkmem_compr; void init(compression algo, U_I compression_level); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/limitint.cpp0000644000175000017500000000231614636066467013702 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "erreurs.hpp" #include "infinint.hpp" #include "generic_file.hpp" namespace libdar { // nothing more here, since pragma interface/implementation has been removed } // end of namespace dar-2.7.15/src/libdar/Makefile.am0000644000175000017500000003412614636067146013400 00000000000000####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### if BSD_SED SED_REGEX = -E else SED_REGEX = -r endif if PROFILING LD_PROF = -pg CPP_PROF = -pg clean-local: rm -f gmon.out rm -f libdar.pc else LD_PROF = CPP_PROF = clean-local: rm -f libdar.pc endif if WITH_LIBTHREADAR LIBTHREADAR_DEP_MODULES=parallel_tronconneuse.cpp parallel_block_compressor.cpp else LIBTHREADAR_DEP_MODULES= endif if BUILD_MODE32 MYLIB=libdar32.la AM_CPPFLAGS=-DLIBDAR_MODE=32 -DDAR_LOCALEDIR=\"$(localedir)\" $(CPP_PROF) VER_SRC=limitint.o LIBDAR_SUFFIX=32 LIBDAR_MODE=-DLIBDAR_MODE=32 else if BUILD_MODE64 MYLIB=libdar64.la AM_CPPFLAGS=-DLIBDAR_MODE=64 -DDAR_LOCALEDIR=\"$(localedir)\" $(CPP_PROF) VER_SRC=limitint.o LIBDAR_SUFFIX=64 LIBDAR_MODE=-DLIBDAR_MODE=64 else MYLIB=libdar.la AM_CPPFLAGS= -DDAR_LOCALEDIR=\"$(localedir)\" $(CPP_PROF) VER_SRC=real_infinint.o LIBDAR_SUFFIX= LIBDAR_MODE= endif endif AM_LDFLAGS = $(LTLIBINTL) $(LD_PROF) lib_LTLIBRARIES = $(MYLIB) LIBDAR_MAJOR=`grep LIBDAR_COMPILE_TIME_MAJOR '$(srcdir)/get_version.hpp' | cut -f 2 -d "=" | cut -f 1 -d ';' | sed -e 's% %%g'` LIBDAR_MEDIUM=`grep LIBDAR_COMPILE_TIME_MEDIUM '$(srcdir)/get_version.hpp' | cut -f 2 -d "=" | cut -f 1 -d ';' | sed -e 's% %%g'` LIBDAR_MEDIUM_000=`grep LIBDAR_COMPILE_TIME_MEDIUM '$(srcdir)/get_version.hpp' | cut -f 2 -d "=" | cut -f 1 -d ';' | sed $(SED_REGEX) -e 's% %%g' -e 's%([^0-9]|^)([0-9]{1})([^0-9]|$$)%0\2%' -e 's%([^0-9]|^)([0-9]{2})([^0-9]|$$)%0\2%'` LIBDAR_MINOR=`grep LIBDAR_COMPILE_TIME_MINOR '$(srcdir)/get_version.hpp' | cut -f 2 -d "=" | cut -f 1 -d ';' | sed -e 's% %%g'` LIBDAR_LIBTOOL_CURRENT=$(LIBDAR_MAJOR)$(LIBDAR_MEDIUM_000) LIBDAR_LIBTOOL_REVISION=$(LIBDAR_MINOR) LIBDAR_LIBTOOL_AGE=$(LIBDAR_MEDIUM) LIBDAR_VERSION_IN=$(LIBDAR_LIBTOOL_CURRENT):$(LIBDAR_LIBTOOL_REVISION):$(LIBDAR_LIBTOOL_AGE) LIBDAR_VERSION_OUT=$(LIBDAR_MAJOR).$(LIBDAR_MEDIUM).$(LIBDAR_MINOR) # header files required by external applications and that must be installed (make install) dist_noinst_DATA = libdar.hpp archive.hpp database.hpp libdar_xform.hpp libdar_slave.hpp erreurs.hpp compile_time_features.hpp entrepot_libcurl.hpp get_version.hpp archive_options_listing_shell.hpp shell_interaction.hpp user_interaction_callback.hpp user_interaction_blind.hpp path.hpp statistics.hpp archive_options.hpp list_entry.hpp crypto.hpp archive_summary.hpp archive_listing_callback.hpp user_interaction.hpp database_options.hpp database_archives.hpp archive_num.hpp database_listing_callback.hpp infinint.hpp archive_aux.hpp integers.hpp entrepot.hpp secu_string.hpp mycurl_protocol.hpp deci.hpp mask.hpp mask_list.hpp crit_action.hpp fsa_family.hpp compression.hpp real_infinint.hpp datetime.hpp range.hpp cat_status.hpp ea.hpp entree_stats.hpp database_aux.hpp limitint.hpp gf_mode.hpp criterium.hpp int_tools.hpp proto_generic_file.hpp storage.hpp archive5.hpp archive_options5.hpp database5.hpp entrepot_libcurl5.hpp libdar5.hpp user_interaction5.hpp user_interaction_callback5.hpp shell_interaction_emulator.hpp memory_file.hpp tlv.hpp tlv_list.hpp fichier_global.hpp mem_ui.hpp entrepot_local.hpp etage.hpp data_tree.hpp tuyau.hpp tools.hpp compressor.hpp generic_file.hpp crc.hpp wrapperlib.hpp thread_cancellation.hpp capabilities.hpp fichier_local.hpp delta_sig_block_size.hpp proto_compressor.hpp parallel_block_compressor.hpp block_compressor.hpp compressor_zstd.hpp filesystem_ids.hpp install-data-local: mkdir -p $(DESTDIR)$(pkgincludedir) cp ../../config.h $(DESTDIR)$(pkgincludedir)/libdar_config.h sed -e 's%config.h%libdar_config.h%g' -e 's%HAVE_CONFIG_H%1%g' -e 's%MY_CONFIG_H%LIBDAR_MY_CONFIG_H%g' '$(srcdir)/../my_config.h' > ./libdar_my_config.h cp '$(srcdir)/../gettext.h' . for file in $(dist_noinst_DATA) gettext.h libdar_my_config.h ; do if [ -e $(srcdir)/$$file ] ; then src=$(srcdir)/$$file ; else src=$$file ; fi ; sed -e 's%#include \"../my_config.h\"%INC_MY_CONFIG_FILE_H%g' -e "s%#include \"%#include \"$(pkgincludedir)/%g" -e "s%INC_MY_CONFIG_FILE_H%#include \"$(pkgincludedir)/libdar_my_config.h\"%g" "$$src" > $(DESTDIR)$(pkgincludedir)/"$$file" ; done rm libdar_my_config.h gettext.h for file in $(DESTDIR)$(pkgincludedir)/* ; do sed -e 's%HAVE_%LIBDAR_HAS_%g' -e 's%PACKAGE%DAR_PACKAGE%g' -e 's%SIZEOF_%LIBDAR_SIZEOF_%g' -e 's%LSTAT_FOLLOWS_SLASHED_SYMLINK%LIBDAR_LSTAT_FOLLOWS_SLASHED_SYMLINK%g' -e 's%VERSION%LIBDAR_VERSION%g' -e 's%MUTEX_WORKS%LIBDAR_MUTEX_WORKS%g' -e 's%OS_BITS%LIBDAR_OS_BITS%g' -e 's%_AVAILABLE%_AVAILABLE_FOR_LIBDAR%g' -e 's%STDC_HEADERS%LIBDAR_STDC_HEADERS%g' -e 's%ENABLE_NLS%DAR_ENABLE_NLS%g' -e 's%HAVE_GETTEXT%DAR_HAS_GETTEXT%g' "$$file" > "$$file.tmp" && mv "$$file.tmp" "$$file" ; done egrep 'LIBDAR|DAR_' $(DESTDIR)$(pkgincludedir)/libdar_config.h | grep -v "#undef" > $(DESTDIR)$(pkgincludedir)/config.h.tmp mv $(DESTDIR)$(pkgincludedir)/config.h.tmp $(DESTDIR)$(pkgincludedir)/libdar_config.h chmod 0644 $(DESTDIR)$(pkgincludedir)/* chmod 0755 $(DESTDIR)$(pkgincludedir) $(INSTALL) -d $(DESTDIR)$(pkgconfigdir) $(INSTALL) -m 0644 libdar.pc $(DESTDIR)$(pkgconfigdir)/libdar$(LIBDAR_SUFFIX).pc uninstall-local: rm -rf $(DESTDIR)$(pkgincludedir) $(DESTDIR)$(pkgconfigdir)/libdar$(LIBDAR_SUFFIX).pc all-local : libdar.pc libdar.pc : libdar.pc.tmpl sed -e "s%#LIBDAR_VERSION#%$(LIBDAR_VERSION_OUT)%g" -e "s%#LIBDAR_SUFFIX#%$(LIBDAR_SUFFIX)%g" -e "s%#LIBDAR_MODE#%$(LIBDAR_MODE)%g" -e "s%#CXXFLAGS#%$(CXXFLAGS)%g" -e "s%#CXXSTDFLAGS#%$(CXXSTDFLAGS)%g" libdar.pc.tmpl > libdar.pc # header files that are internal to libdar and that must not be installed (make install) noinst_HEADERS = archive_version.hpp cache_global.hpp cache.hpp candidates.hpp cat_all_entrees.hpp catalogue.hpp cat_blockdev.hpp cat_chardev.hpp cat_delta_signature.hpp cat_detruit.hpp cat_device.hpp cat_directory.hpp cat_door.hpp cat_entree.hpp cat_eod.hpp cat_etoile.hpp cat_file.hpp cat_ignored_dir.hpp cat_ignored.hpp cat_inode.hpp cat_lien.hpp cat_mirage.hpp cat_nomme.hpp cat_prise.hpp cat_signature.hpp cat_tube.hpp contextual.hpp crypto_asym.hpp crypto_sym.hpp cygwin_adapt.hpp cygwin_adapt.h database_header.hpp data_dir.hpp defile.hpp ea_filesystem.hpp elastic.hpp entrepot_libcurl.hpp erreurs_ext.hpp escape_catalogue.hpp escape.hpp fichier_libcurl.hpp filesystem_backup.hpp filesystem_diff.hpp filesystem_hard_link_read.hpp filesystem_hard_link_write.hpp filesystem_restore.hpp filesystem_specific_attribute.hpp filesystem_tools.hpp filtre.hpp generic_file_overlay_for_gpgme.hpp generic_rsync.hpp generic_to_global_file.hpp hash_fichier.hpp header.hpp header_version.hpp i_archive.hpp i_database.hpp i_entrepot_libcurl.hpp i_libdar_xform.hpp label.hpp macro_tools.hpp mycurl_easyhandle_node.hpp mycurl_easyhandle_sharing.hpp nls_swap.hpp null_file.hpp op_tools.hpp pile_descriptor.hpp pile.hpp sar.hpp sar_tools.hpp scrambler.hpp secu_memory_file.hpp semaphore.hpp shell_interaction_emulator.hpp slave_zapette.hpp slice_layout.hpp smart_pointer.hpp sparse_file.hpp terminateur.hpp trivial_sar.hpp tronc.hpp tronconneuse.hpp trontextual.hpp user_group_bases.hpp zapette.hpp zapette_protocol.hpp mem_block.hpp parallel_tronconneuse.hpp crypto_segment.hpp crypto_module.hpp proto_tronco.hpp compress_module.hpp lz4_module.hpp gzip_module.hpp bzip2_module.hpp lzo_module.hpp zstd_module.hpp xz_module.hpp compress_block_header.hpp header_flags.hpp mycurl_param_list.hpp mycurl_slist.hpp tuyau_global.hpp ALL_SOURCES = archive5.cpp archive5.hpp archive_aux.cpp archive_aux.hpp archive.cpp archive.hpp archive_listing_callback.hpp archive_num.cpp archive_num.hpp archive_options5.hpp archive_options.cpp archive_options.hpp archive_options_listing_shell.cpp archive_options_listing_shell.hpp archive_summary.cpp archive_summary.hpp archive_version.cpp archive_version.hpp cache.cpp cache_global.cpp cache_global.hpp cache.hpp candidates.cpp candidates.hpp capabilities.cpp capabilities.hpp cat_all_entrees.hpp catalogue.cpp catalogue.hpp cat_blockdev.cpp cat_blockdev.hpp cat_chardev.cpp cat_chardev.hpp cat_delta_signature.cpp cat_delta_signature.hpp cat_detruit.cpp cat_detruit.hpp cat_device.cpp cat_device.hpp cat_directory.cpp cat_directory.hpp cat_door.cpp cat_door.hpp cat_entree.cpp cat_entree.hpp cat_eod.hpp cat_etoile.cpp cat_etoile.hpp cat_file.cpp cat_file.hpp cat_ignored.cpp cat_ignored_dir.cpp cat_ignored_dir.hpp cat_ignored.hpp cat_inode.cpp cat_inode.hpp cat_lien.cpp cat_lien.hpp cat_mirage.cpp cat_mirage.hpp cat_nomme.cpp cat_nomme.hpp cat_prise.cpp cat_prise.hpp cat_signature.cpp cat_signature.hpp cat_status.hpp cat_tube.cpp cat_tube.hpp compile_time_features.cpp compile_time_features.hpp compression.cpp compression.hpp compressor.cpp compressor.hpp contextual.cpp contextual.hpp crc.cpp crc.hpp crit_action.cpp crit_action.hpp criterium.cpp criterium.hpp crypto_asym.cpp crypto_asym.hpp crypto.cpp crypto.hpp crypto_sym.cpp crypto_sym.hpp cygwin_adapt.hpp cygwin_adapt.h database5.cpp database5.hpp database_archives.hpp database_aux.hpp database.cpp database_header.cpp database_header.hpp database.hpp database_listing_callback.hpp database_options.hpp data_dir.cpp data_dir.hpp data_tree.cpp data_tree.hpp datetime.cpp datetime.hpp deci.cpp deci.hpp defile.cpp defile.hpp ea.cpp ea_filesystem.cpp ea_filesystem.hpp ea.hpp elastic.cpp elastic.hpp entree_stats.cpp entree_stats.hpp entrepot.cpp entrepot.hpp entrepot_libcurl5.hpp entrepot_libcurl.hpp entrepot_local.cpp entrepot_local.hpp erreurs.cpp erreurs_ext.cpp erreurs_ext.hpp erreurs.hpp escape_catalogue.cpp escape_catalogue.hpp escape.cpp escape.hpp etage.cpp etage.hpp fichier_global.cpp fichier_global.hpp fichier_local.cpp fichier_local.hpp filesystem_backup.cpp filesystem_backup.hpp filesystem_diff.cpp filesystem_diff.hpp filesystem_hard_link_read.cpp filesystem_hard_link_read.hpp filesystem_hard_link_write.cpp filesystem_hard_link_write.hpp filesystem_restore.cpp filesystem_restore.hpp filesystem_specific_attribute.cpp filesystem_specific_attribute.hpp filesystem_tools.cpp filesystem_tools.hpp filtre.cpp filtre.hpp fsa_family.cpp fsa_family.hpp generic_file.cpp generic_file.hpp generic_file_overlay_for_gpgme.cpp generic_file_overlay_for_gpgme.hpp generic_rsync.cpp generic_rsync.hpp generic_to_global_file.hpp get_version.cpp get_version.hpp gf_mode.cpp gf_mode.hpp hash_fichier.cpp hash_fichier.hpp header.cpp header.hpp header_version.cpp header_version.hpp i_archive.cpp i_archive.hpp i_database.cpp i_database.hpp i_entrepot_libcurl.hpp i_libdar_xform.cpp i_libdar_xform.hpp infinint.hpp integers.cpp integers.hpp int_tools.cpp int_tools.hpp label.cpp label.hpp libdar5.cpp libdar5.hpp libdar.hpp libdar_slave.cpp libdar_slave.hpp libdar_xform.cpp libdar_xform.hpp limitint.hpp list_entry.cpp list_entry.hpp macro_tools.cpp macro_tools.hpp mask.cpp mask.hpp mask_list.cpp mask_list.hpp memory_file.cpp memory_file.hpp mem_ui.cpp mem_ui.hpp mycurl_easyhandle_node.cpp mycurl_easyhandle_node.hpp mycurl_easyhandle_sharing.cpp mycurl_easyhandle_sharing.hpp mycurl_protocol.cpp mycurl_protocol.hpp nls_swap.hpp null_file.hpp op_tools.cpp op_tools.hpp path.cpp path.hpp pile.cpp pile_descriptor.cpp pile_descriptor.hpp pile.hpp proto_generic_file.hpp range.cpp range.hpp real_infinint.hpp sar.cpp sar.hpp sar_tools.cpp sar_tools.hpp scrambler.cpp scrambler.hpp secu_memory_file.cpp secu_memory_file.hpp secu_string.cpp secu_string.hpp semaphore.cpp semaphore.hpp shell_interaction.cpp shell_interaction_emulator.cpp shell_interaction_emulator.hpp shell_interaction.hpp slave_zapette.cpp slave_zapette.hpp slice_layout.cpp slice_layout.hpp smart_pointer.hpp sparse_file.cpp sparse_file.hpp statistics.cpp statistics.hpp storage.cpp storage.hpp terminateur.cpp terminateur.hpp thread_cancellation.cpp thread_cancellation.hpp tlv.cpp tlv.hpp tlv_list.cpp tlv_list.hpp tools.cpp tools.hpp trivial_sar.cpp trivial_sar.hpp tronc.cpp tronc.hpp tronconneuse.cpp tronconneuse.hpp trontextual.cpp trontextual.hpp tuyau.cpp tuyau.hpp user_group_bases.cpp user_group_bases.hpp user_interaction5.cpp user_interaction5.hpp user_interaction_blind.cpp user_interaction_blind.hpp user_interaction_callback5.cpp user_interaction_callback5.hpp user_interaction_callback.cpp user_interaction_callback.hpp user_interaction.cpp user_interaction.hpp wrapperlib.cpp wrapperlib.hpp zapette.cpp zapette.hpp zapette_protocol.cpp zapette_protocol.hpp entrepot_libcurl.cpp fichier_libcurl.cpp i_entrepot_libcurl.cpp delta_sig_block_size.cpp mem_block.hpp mem_block.cpp heap.hpp parallel_tronconneuse.hpp crypto_module.hpp proto_compressor.hpp parallel_block_compressor.hpp compress_module.hpp lz4_module.hpp lz4_module.cpp block_compressor.cpp block_compressor.hpp gzip_module.hpp gzip_module.cpp bzip2_module.hpp bzip2_module.cpp lzo_module.hpp lzo_module.cpp zstd_module.hpp zstd_module.cpp xz_module.hpp xz_module.cpp compressor_zstd.hpp compressor_zstd.cpp compress_block_header.hpp compress_block_header.cpp header_flags.hpp header_flags.cpp filesystem_ids.cpp filesystem_ids.hpp mycurl_param_list.hpp mycurl_param_list.cpp mycurl_slist.hpp mycurl_slist.cpp tuyau_global.hpp tuyau_global.cpp libdar_la_LDFLAGS = -version-info $(LIBDAR_VERSION_IN) libdar_la_SOURCES = $(ALL_SOURCES) real_infinint.cpp $(LIBTHREADAR_DEP_MODULES) libdar32_la_LDFLAGS = -version-info $(LIBDAR_VERSION_IN) libdar32_la_SOURCES = $(ALL_SOURCES) limitint.cpp $(LIBTHREADAR_DEP_MODULES) libdar64_la_LDFLAGS = -version-info $(LIBDAR_VERSION_IN) libdar64_la_SOURCES = $(ALL_SOURCES) limitint.cpp $(LIBTHREADAR_DEP_MODULES) dar-2.7.15/src/libdar/cat_delta_signature.cpp0000644000175000017500000002303414636066467016052 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "tronc.hpp" #include "cat_delta_signature.hpp" #include "tools.hpp" using namespace std; namespace libdar { cat_delta_signature::cat_delta_signature(generic_file *f, proto_compressor *c) { init(); src = f; zip = c; if(src == nullptr) throw SRC_BUG; if(zip == nullptr) throw SRC_BUG; pending_read = true; } void cat_delta_signature::read(bool sequential_read, const archive_version & ver) { if(src == nullptr) throw SRC_BUG; try { if(ver < archive_version(11,2)) patch_base_check = create_crc_from_file(*src); // starting format 10.2 the patch base check // has been moved before the delta patch, // while this cat_delta_structure stays written // after the delta patch and its CRC // To patch_base_check is since then set // calling dump_patch_base_crc() else { if(patch_base_check != nullptr) { delete patch_base_check; patch_base_check = nullptr; } } delta_sig_size.read(*src); if(!delta_sig_size.is_zero()) { if(sequential_read) { delta_sig_offset = src->get_position(); fetch_data(ver); } else delta_sig_offset.read(*src); } patch_result_check = create_crc_from_file(*src); pending_read = false; } catch(...) { clear(); throw; } } std::shared_ptr cat_delta_signature::obtain_sig(const archive_version & ver) const { if(delta_sig_size.is_zero()) throw SRC_BUG; if(!sig) { if(src == nullptr) throw SRC_BUG; fetch_data(ver); if(!sig) throw SRC_BUG; // fetch_data() failed but did not raised any exception } return sig; } void cat_delta_signature::set_sig(const std::shared_ptr & ptr, U_I sig_block_size) { if(!ptr) throw SRC_BUG; sig = ptr; delta_sig_size = sig->size(); if(delta_sig_size.is_zero()) throw SRC_BUG; sig_block_len = sig_block_size; if(sig_block_len == 0) throw SRC_BUG; } void cat_delta_signature::dump_data(generic_file & f, bool sequential_mode, const archive_version & ver) const { // fetching the data if it is missing if(!delta_sig_size.is_zero()) { if(!sig) fetch_data(ver); } // dumping data if(sequential_mode) { //if(!has_patch_base_crc()) // throw SRC_BUG; // patch_base_check->dump(f); ///// since format 11.2 we do not save patch_base_crc ///// has been moved to cat_file ///// to be saved before the delta patch and ///// allow patching a file when reading a backup ///// in sequential mode ///// the field is still present in this class ///// for backward compatibility with older format delta_sig_size.dump(f); } if(!delta_sig_size.is_zero()) { infinint crc_size = tools_file_size_to_crc_size(delta_sig_size); crc *calculated = nullptr; cat_delta_signature *me = const_cast(this); try { me->delta_sig_offset = f.get_position(); infinint(sig_block_len).dump(f); if(!sig) throw SRC_BUG; sig->skip(0); sig->copy_to(f, crc_size, calculated); if(calculated == nullptr) throw SRC_BUG; calculated->dump(f); } catch(...) { if(calculated != nullptr) delete calculated; throw; } if(calculated != nullptr) delete calculated; } if(sequential_mode) { if(!has_patch_result_crc()) throw SRC_BUG; patch_result_check->dump(f); } } void cat_delta_signature::dump_metadata(generic_file & f) const { // if(!has_patch_base_crc()) // throw SRC_BUG; // patch_base_check->dump(f); ///// patch_base_check has moved to cat_file ///// since format 11.2 delta_sig_size.dump(f); if(!delta_sig_size.is_zero()) delta_sig_offset.dump(f); if(!has_patch_result_crc()) throw SRC_BUG; patch_result_check->dump(f); } bool cat_delta_signature::get_patch_base_crc(const crc * & c) const { if(patch_base_check != nullptr) { c = patch_base_check; return true; } else return false; } void cat_delta_signature::set_patch_base_crc(const crc & c) { throw SRC_BUG; // no more used since format 11.2 } bool cat_delta_signature::get_patch_result_crc(const crc * & c) const { if(patch_result_check != nullptr) { c = patch_result_check; return true; } else return false; } void cat_delta_signature::set_patch_result_crc(const crc & c) { if(patch_result_check != nullptr) { delete patch_result_check; patch_result_check = nullptr; } patch_result_check = c.clone(); if(patch_result_check == nullptr) throw Ememory("cat_delta_signature::set_crc"); } void cat_delta_signature::init() noexcept { patch_base_check = nullptr; delta_sig_size = 0; delta_sig_offset = 0; sig.reset(); patch_result_check = nullptr; src = nullptr; zip = nullptr; sig_block_len = 0; pending_read = false; } void cat_delta_signature::copy_from(const cat_delta_signature & ref) { delta_sig_offset = ref.delta_sig_offset; delta_sig_size = ref.delta_sig_size; sig = ref.sig; if(ref.patch_base_check != nullptr) { patch_base_check = ref.patch_base_check->clone(); if(patch_base_check == nullptr) throw Ememory("cat_delta_signature::copy_from"); } else patch_base_check = nullptr; if(ref.patch_result_check != nullptr) { patch_result_check = ref.patch_result_check->clone(); if(patch_result_check == nullptr) throw Ememory("cat_delta_signature::copy_from"); } else patch_result_check = nullptr; src = ref.src; zip = ref.zip; pending_read = ref.pending_read; } void cat_delta_signature::move_from(cat_delta_signature && ref) noexcept { delta_sig_offset = move(ref.delta_sig_offset); delta_sig_size = move(ref.delta_sig_size); // we can swap the memory file, because sig_is_ours is swapped // too and we will known when destroying ref whether we own // the object pointed to by sig or not sig.swap(ref.sig); swap(patch_base_check, ref.patch_base_check); swap(patch_result_check, ref.patch_result_check); src = move(ref.src); zip = move(ref.zip); pending_read = move(ref.pending_read); } void cat_delta_signature::destroy() noexcept { if(patch_base_check != nullptr) { delete patch_base_check; patch_base_check = nullptr; } sig.reset(); if(patch_result_check != nullptr) { delete patch_result_check; patch_result_check = nullptr; } src = nullptr; zip = nullptr; } void cat_delta_signature::fetch_data(const archive_version & ver) const { if(!delta_sig_size.is_zero() && delta_sig_offset.is_zero()) throw SRC_BUG; if(delta_sig_size.is_zero()) return; if(sig == nullptr) // we have to fetch the data { crc *calculated = nullptr; crc *delta_sig_crc = nullptr; if(src == nullptr) throw SRC_BUG; if(zip == nullptr) throw SRC_BUG; // need to suspend compression before reading the data zip->suspend_compression(); try { src->skip(delta_sig_offset); if(ver >= archive_version(10,1)) { // we need first to read the block len used to build the signature infinint tmp(*src); sig_block_len = 0; tmp.unstack(sig_block_len); if(!tmp.is_zero()) throw Erange("cat_delta_signature::fetch_data", gettext("data corrupted when attempting to read delta signature block size")); } else sig_block_len = 2048; // RS_DEFAULT_BLOCK_LEN from librsync. Using value in case this macro would change in the future // now we can read the delta signature itself tronc bounded(src, src->get_position(), delta_sig_size, false); infinint crc_size = tools_file_size_to_crc_size(delta_sig_size); sig.reset(new (nothrow) memory_file()); if(!sig) throw Ememory("cat_delta_signature::read"); bounded.skip(0); bounded.copy_to(*sig, crc_size, calculated); if(calculated == nullptr) throw SRC_BUG; sig->skip(0); delta_sig_crc = create_crc_from_file(*src); if(delta_sig_crc == nullptr) throw Erange("cat_delta_signature::fetch_data", gettext("Error while reading CRC of delta signature data. Data corruption occurred")); if(*delta_sig_crc != *calculated) throw Erange("cat_delta_signature::read_data", gettext("CRC error met while reading delta signature: data corruption.")); } catch(...) { if(calculated != nullptr) delete calculated; if(delta_sig_crc != nullptr) delete delta_sig_crc; sig.reset(); throw; } if(calculated != nullptr) delete calculated; if(delta_sig_crc != nullptr) delete delta_sig_crc; } } } // end of namespace dar-2.7.15/src/libdar/cat_inode.hpp0000644000175000017500000002320714636066467014005 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_inode.hpp /// \brief base object for all inode types, managed EA and FSA, dates, permissions, ownership, ... /// \ingroup Private #ifndef CAT_INODE_HPP #define CAT_INODE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "infinint.hpp" #include "ea.hpp" #include "integers.hpp" #include "mask.hpp" #include "user_interaction.hpp" #include "filesystem_specific_attribute.hpp" #include "datetime.hpp" #include "cat_nomme.hpp" #include "archive_aux.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the root class for all cat_inode class cat_inode : public cat_nomme { public: /// flag used to only consider certain fields when comparing/restoring inodes cat_inode(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & xname, const infinint & device); cat_inode(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small); cat_inode(const cat_inode & ref); cat_inode(cat_inode && ref) noexcept: cat_nomme(std::move(ref)) { nullifyptr(); move_from(std::move(ref)); }; cat_inode & operator = (const cat_inode & ref); cat_inode & operator = (cat_inode && ref) { cat_nomme::operator = (std::move(ref)); move_from(std::move(ref)); return *this; }; ~cat_inode() noexcept(false); const infinint & get_uid() const { return uid; }; const infinint & get_gid() const { return gid; }; U_16 get_perm() const { return perm; }; datetime get_last_access() const { return last_acc; }; datetime get_last_modif() const { return last_mod; }; void set_last_access(const datetime & x_time) { last_acc = x_time; }; void set_last_modif(const datetime & x_time) { last_mod = x_time; }; infinint get_device() const { if(fs_dev == nullptr) throw SRC_BUG; return *fs_dev; }; bool same_as(const cat_inode & ref) const; bool is_more_recent_than(const cat_inode & ref, const infinint & hourshift) const; // used for RESTORATION virtual bool has_changed_since(const cat_inode & ref, const infinint & hourshift, comparison_fields what_to_check) const; // signature() left as an abstract method // clone is abstract too // used for INCREMENTAL BACKUP void compare(const cat_inode &other, const mask & ea_mask, comparison_fields what_to_check, const infinint & hourshift, bool symlink_date, const fsa_scope & scope, bool isolated_mode) const; ///< do not try to compare pointed to data, EA of FSA (suitable for isolated catalogue) // throw Erange exception if a difference has been detected // this is not a symetrical comparison, but all what is present // in the current object is compared against the argument // which may contain supplementary informations // used for DIFFERENCE ////////////////////////////////// // EXTENDED ATTRIBUTES Methods // // I : to know whether EA data is present or not for this object void ea_set_saved_status(ea_saved_status status); ea_saved_status ea_get_saved_status() const { return ea_saved; }; // II : to associate EA list to an cat_inode object (mainly for backup operation) #EA_FULL only# void ea_attach(ea_attributs *ref); /// the returned value is the address of an existing file of the cat_inode object and shall not be deleted by the caller const ea_attributs *get_ea() const; // #<-- EA_FULL *and* EA_REMOVED# for this call only void ea_detach() const; //discards any future call to get_ea() ! infinint ea_get_size() const; //returns the size of EA (still valid if ea have been detached) mainly used to define CRC width // III : to record where is dump the EA in the archive #EA_FULL only# void ea_set_offset(const infinint & pos); bool ea_get_offset(infinint & pos) const; void ea_set_crc(const crc & val); void ea_get_crc(const crc * & ptr) const; ///< the argument is set to point to an allocated crc object owned by this "cat_inode" object, this reference stays valid while the "cat_inode" object exists and MUST NOT be deleted by the caller in any case bool ea_get_crc_size(infinint & val) const; ///< returns true if crc is know and puts its width in argument // IV : to know/record if EA and FSA have been modified # any EA status# and FSA status # datetime get_last_change() const { return last_cha; }; void set_last_change(const datetime & x_time) { last_cha = x_time; }; bool has_last_change() const { return !last_cha.is_null(); }; // old format did provide last_change only when EA were present, since archive // format 8, this field is always present even in absence of EA. Thus it is // still necessary to check if the cat_inode has a last_change() before // using get_last_change() (depends on the version of the archive read). ////////////////////////////////// // FILESYSTEM SPECIFIC ATTRIBUTES Methods // // I : which FSA are present void fsa_set_saved_status(fsa_saved_status status); fsa_saved_status fsa_get_saved_status() const { return fsa_saved; }; /// gives the set of FSA family recorded for that inode fsa_scope fsa_get_families() const { if(fsa_families == nullptr) throw SRC_BUG; return infinint_to_fsa_scope(*fsa_families); }; // II : add or drop FSA list to the cat_inode void fsa_attach(filesystem_specific_attribute_list *ref); void fsa_partial_attach(const fsa_scope & val); // #<-- FSA_PARTIAL only const filesystem_specific_attribute_list *get_fsa() const; // #<-- FSA_FULL only void fsa_detach() const; // discard any future call to get_fsa() ! infinint fsa_get_size() const; // returns the size of FSA (still valid if fsal has been detached) / mainly used to define CRC size // III : to record where FSA are dumped in the archive (only if fsa_status not empty !) void fsa_set_offset(const infinint & pos); bool fsa_get_offset(infinint & pos) const; void fsa_set_crc(const crc & val); void fsa_get_crc(const crc * & ptr) const; bool fsa_get_crc_size(infinint & val) const; protected: virtual void sub_compare(const cat_inode & other, bool isolated_mode) const {}; bool get_small_read() const { return small_read; }; ///< true if object has been created by sequential reading of an archive // inherited from cat_entree virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; private : infinint uid; ///< inode owner's user ID infinint gid; ///< inode owner's group ID U_16 perm; ///< inode's permission datetime last_acc; ///< last access time (atime) datetime last_mod; ///< last modification time (mtime) datetime last_cha; ///< last inode meta data change (ctime) ea_saved_status ea_saved; ///< inode Extended Attribute status fsa_saved_status fsa_saved; ///< inode Filesystem Specific Attribute status bool small_read; ///< whether we the object has been built with sequential-reading // the following is used only if ea_saved == full infinint *ea_offset; ///< offset in archive where to find EA ea_attributs *ea; ///< Extended Attributes read or to be written down infinint *ea_size; ///< storage size required by EA // the following is used if ea_saved == full or ea_saved == partial or crc *ea_crc; ///< CRC computed on EA infinint *fsa_families; ///< list of FSA families present for that inode (set to nullptr in fsa_none mode) infinint *fsa_offset; ///< offset in archive where to find FSA # always allocated (to be reviewed) filesystem_specific_attribute_list *fsal; ///< Filesystem Specific Attributes read or to be written down # only allocated if fsa_saved if set to FULL infinint *fsa_size; ///< storage size required for FSA crc *fsa_crc; ///< CRC computed on FSA // infinint *fs_dev; ///< filesystem ID on which resides the inode (only used when read from filesystem) archive_version edit; ///< need to know EA and FSA format used in archive file void nullifyptr() noexcept; void destroy() noexcept; void copy_from(const cat_inode & ref); void move_from(cat_inode && ref) noexcept; static const ea_attributs empty_ea; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/capabilities.hpp0000644000175000017500000000407714636066467014515 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file capabilities.hpp /// \brief provide information about current thread (underlying using the widthdrawn POSIX.1e API) /// \ingroup Private #ifndef CAPABILITIES_HPP #define CAPABILITIES_HPP #include "../my_config.h" #include "user_interaction.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the EFFECTIVE set the value of the associated capability for the calling thread /// returned by the capability_* routines enum capa_status { capa_set, ///< current thread has the requested capability capa_clear, ///< current thread has not the requested capability capa_unknown }; ///< impossible to determine whether the current thread has the requested capability extern capa_status capability_LINUX_IMMUTABLE(user_interaction & ui, bool verbose); extern capa_status capability_SYS_RESOURCE(user_interaction & ui, bool verbose); extern capa_status capability_FOWNER(user_interaction & ui, bool verbose); extern capa_status capability_CHOWN(user_interaction & ui, bool verbose); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mem_block.hpp0000644000175000017500000000477314636066467014017 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mem_block.hpp /// \brief structure to hold block of memory and manipulate in coherence with idexes and sizes /// \ingroup Private /// #ifndef MEM_BLOCK_HPP #define MEM_BLOCK_HPP #include "../my_config.h" #include #include "integers.hpp" namespace libdar { /// \addtogroup Private /// @{ class mem_block { public: mem_block(U_I size = 0); mem_block(const mem_block & ref) = delete; mem_block(mem_block && ref) noexcept; mem_block & operator = (const mem_block & ref) = delete; mem_block & operator = (mem_block && ref) noexcept; virtual ~mem_block(); void resize(U_I size); U_I read(char *a, U_I size); ///< read data from the mem_block, returns the amount read U_I write(const char *a, U_I size); ///< write data to the mem_block, returns the amount wrote void rewind_read(U_I offset = 0); ///< reset read cursor void reset() { data_size = 0; read_cursor = 0; write_cursor = 0; }; U_I get_max_size() const { return alloc_size; }; U_I get_data_size() const { return data_size; }; U_I get_read_offset() const { return read_cursor; }; bool all_is_read() const { return read_cursor == data_size; }; bool is_full() const { return data_size == alloc_size; }; bool is_empty() const { return data_size == 0; }; char* get_addr() { return data; }; void set_data_size(U_I size); private: char* data; U_I alloc_size; U_I data_size; U_I read_cursor; U_I write_cursor; void move_from(mem_block && ref); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mask.cpp0000644000175000017500000001652014636066467013006 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_FNMATCH_H #include #endif } // end extern "C" #include "mask.hpp" #include "tools.hpp" #include "erreurs.hpp" using namespace std; namespace libdar { static string bool2_sensitivity(bool case_s); simple_mask::simple_mask(const string & wilde_card_expression, bool case_sensit) : case_s(case_sensit) { if(!case_s) tools_to_upper(wilde_card_expression, the_mask); else the_mask = wilde_card_expression; } bool simple_mask::is_covered(const string &expression) const { if(!case_s) { string upper; tools_to_upper(expression, upper); return fnmatch(the_mask.c_str(), upper.c_str(), FNM_PERIOD) == 0; } else return fnmatch(the_mask.c_str(), expression.c_str(), FNM_PERIOD) == 0; } string simple_mask::dump(const string & prefix) const { string sensit = bool2_sensitivity(case_s); return tools_printf(gettext("%Sglob expression: %S [%S]"), &prefix, &the_mask, &sensit); } regular_mask::regular_mask(const string & wilde_card_expression, bool x_case_sensit) { mask_exp = wilde_card_expression; case_sensit = x_case_sensit; set_preg(mask_exp, case_sensit); } regular_mask & regular_mask::operator = (const regular_mask & ref) { mask::operator = (ref); detruit(); copy_from(ref); return *this; } regular_mask & regular_mask::operator = (regular_mask && ref) noexcept { mask::operator = (move(ref)); detruit(); move_from(move(ref)); return *this; } bool regular_mask::is_covered(const string & expression) const { return regexec(&preg, expression.c_str(), 0, nullptr, 0) != REG_NOMATCH; } string regular_mask::dump(const string & prefix) const { string sensit = bool2_sensitivity(case_sensit); return tools_printf(gettext("%Sregular expression: %S [%S]"), &prefix, &mask_exp, &sensit); } void regular_mask::set_preg(const string & wilde_card_expression, bool x_case_sensit) { S_I ret; if((ret = regcomp(&preg, wilde_card_expression.c_str(), REG_NOSUB|(x_case_sensit ? 0 : REG_ICASE)|REG_EXTENDED)) != 0) { constexpr S_I msg_size = 1024; char msg[msg_size]; regerror(ret, &preg, msg, msg_size); throw Erange("regular_mask::regular_mask", msg); } } void regular_mask::copy_from(const regular_mask & ref) { mask_exp = ref.mask_exp; case_sensit = ref.case_sensit; set_preg(mask_exp, case_sensit); } void regular_mask::move_from(regular_mask && ref) noexcept { mask_exp = move(ref.mask_exp); case_sensit = move(ref.case_sensit); preg = ref.preg; // yes we copy the data, not the possibly pointed to data though ref.detruit(); } not_mask & not_mask::operator = (const not_mask & m) { mask::operator = (m); detruit(); copy_from(m); return *this; } string not_mask::dump(const string & prefix) const { string ref_dump = ref->dump(prefix + " "); return tools_printf(gettext("%Snot(\n%S\n%S)"), &prefix, &ref_dump, &prefix); } void not_mask::copy_from(const not_mask &m) { ref = m.ref->clone(); if(ref == nullptr) throw Ememory("not_mask::copy_from(not_mask)"); } void not_mask::copy_from(const mask &m) { ref = m.clone(); if(ref == nullptr) throw Ememory("not_mask::copy_from(mask)"); } void not_mask::move_from(not_mask && m) noexcept { swap(ref, m.ref); } void not_mask::detruit() { if(ref != nullptr) { delete ref; ref = nullptr; } } et_mask & et_mask::operator = (const et_mask &m) { const mask *src = &m; mask *dst = this; *dst = *src; // explicitely invoke the inherited "mask" class's operator = detruit(); copy_from(m); return *this; } void et_mask::add_mask(const mask& toadd) { mask *t = toadd.clone(); if(t != nullptr) lst.push_back(t); else throw Ememory("et_mask::et_mask"); } string et_mask::dump_logical(const string & prefix, const string & boolop) const { deque::const_iterator it = lst.begin(); string recursive_prefix = prefix + " | "; string ret = prefix + boolop + "\n"; while(it != lst.end()) { if(*it == nullptr) throw SRC_BUG; ret += (*it)->dump(recursive_prefix) + "\n"; ++it; } ret += prefix + " +--"; return ret; } void et_mask::copy_from(const et_mask &m) { deque::const_iterator it = m.lst.begin(); mask *tmp; while(it != m.lst.end() && (tmp = (*it)->clone()) != nullptr) { lst.push_back(tmp); ++it; } if(it != m.lst.end()) { detruit(); throw Ememory("et_mask::copy_from"); } } void et_mask::move_from(et_mask && m) noexcept { detruit(); lst = move(m.lst); m.lst.clear(); } void et_mask::detruit() { deque::iterator it = lst.begin(); while(it != lst.end()) { delete *it; *it = nullptr; ++it; } lst.clear(); } bool simple_path_mask::is_covered(const path &ch) const { return ch.is_subdir_of(chemin, case_s) || chemin.is_subdir_of(ch, case_s); } string simple_path_mask::dump(const string & prefix) const { string chem = chemin.display(); string sensit = bool2_sensitivity(case_s); return tools_printf(gettext("%SIs subdir of: %S [%S]"), &prefix, &chem, &sensit); } bool same_path_mask::is_covered(const std::string &ch) const { if(case_s) return ch == chemin; else return tools_is_case_insensitive_equal(ch, chemin); } string same_path_mask::dump(const std::string & prefix) const { string sensit = bool2_sensitivity(case_s); return tools_printf(gettext("%SPath is: %S [%S]"), &prefix, &chemin, &sensit); } string exclude_dir_mask::dump(const std::string & prefix) const { string sensit = bool2_sensitivity(case_s); return tools_printf(gettext("%SPath leads to: %S [%S]"), &prefix, &chemin, &sensit); } static string bool2_sensitivity(bool case_s) { return case_s ? gettext("case sensitive") : gettext("case in-sensitive"); } } // end of namespace dar-2.7.15/src/libdar/tronc.hpp0000644000175000017500000001307614636066467013210 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file tronc.hpp /// \brief defines a limited segment over another generic_file. /// \ingroup Private /// /// This is used to read a part of a file as if it was a real file generating /// end of file behavior when reaching the given length. #ifndef TRONC_HPP #define TRONC_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// makes a segment of a generic_file appear like a real generic_file class tronc : public generic_file { public : /// constructor /// \param f is the file to take the segment from /// \param offset is the position of the beginning of the segment /// \param size is the size of the segment /// \param own_f is true if this object has to own and must destroy the 'f' object at tronc's destruction time tronc(generic_file *f, const infinint &offset, const infinint &size, bool own_f = false); tronc(generic_file *f, const infinint &offset, const infinint &size, gf_mode mode, bool own_f = false); /// other constructor, the end of the segment is the end of the underlying generic_file /// only data before offset is inaccessible tronc(generic_file *f, const infinint &offset, bool own_f = false); tronc(generic_file *f, const infinint &offset, gf_mode mode, bool own_f = false); tronc(const tronc & ref) = delete; tronc(tronc && ref) noexcept = delete; tronc & operator = (const tronc & ref) = delete; tronc & operator = (tronc && ref) = delete; /// destructor ~tronc() { detruit(); }; /// modify the tronc object to zoom on another (size limited) portion of the underlying object void modify(const infinint & new_offset, const infinint & new_size); /// modify the tronc object to zoom on another (size unlimited) portion of the underlying object void modify(const infinint & new_offset); /// modify the tronc object to become transparent and allow unrestricted access to the underlyuing object void modify() { modify(0); }; /// inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override; /// inherited from generic_file virtual bool skip(const infinint & pos) override; /// inherited from generic_file virtual bool skip_to_eof() override; /// inherited from generic_file virtual bool skip_relative(S_I x) override; /// inherited from generic_file virtual bool truncatable(const infinint & pos) const override { return ref->truncatable(start + pos); }; virtual infinint get_position() const override { return current; }; /// when a tronc is used over a compressor, it becomes necessary to disable position check /// /// \note by default, before each read or write, the tronc object check that the underlying /// object is at adhoc position in regard to where the cursor is currently in the tronc. Disabling /// that check let ignore possible position mismatch (which are normal when a compressor is found below) /// while reading or writing but keep seeking the underlying object to the requested position upon any call /// to tronc::skip_* familly methods. void check_underlying_position_while_reading_or_writing(bool mode) { check_pos = mode ; }; protected : /// inherited from generic_file virtual void inherited_read_ahead(const infinint & amount) override; /// inherited from generic_file virtual U_I inherited_read(char *a, U_I size) override; /// inherited from generic_file virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override { ref->sync_write(); } virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override {if(own_ref) ref->terminate(); }; private : infinint start; ///< offset in the global generic file to start at infinint sz; ///< length of the portion to consider generic_file *ref; ///< global generic file of which to take a piece infinint current; ///< inside position of the next read or write bool own_ref; ///< whether we own ref (and must destroy it when no more needed) bool limited; ///< whether the sz argument is to be considered bool check_pos; ///< whether to check and eventually adjust (seek) the position of the underlying layer at each read or write void set_back_current_position(); void detruit() noexcept { if(own_ref && ref != nullptr) delete ref; }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/tuyau.hpp0000644000175000017500000001332514636066467013227 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file tuyau.hpp /// \brief defines the implementation of pipe under the generic_file interface. /// \ingroup Private /// /// mainly used between zapette and slave_zapette, this is a full implementation /// of the generic_file interface that takes care of dead lock when two pipes needs /// to be openned between the same two entities, each having one for reading and the /// other for writing. #ifndef TUYAU_HPP #define TUYAU_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" #include "thread_cancellation.hpp" #include "mem_ui.hpp" namespace libdar { /// \addtogroup Private /// @{ /// pipe implementation under the generic_file interface. class tuyau : public generic_file, public thread_cancellation, protected mem_ui { public: tuyau(const std::shared_ptr & dialog, ///< for user interaction int fd ///< fd is the filedescriptor of a pipe extremity already openned ); tuyau(const std::shared_ptr & dialog, ///< for user interaction int fd, ///< fd is the filedescriptor of a pipe extremity already openned gf_mode mode ///< forces the mode if possible ); tuyau(const std::shared_ptr & dialog, ///< for user interaction const std::string &filename, ///< named pipe to open gf_mode mode ///< forces the mode if possible ); tuyau(const std::shared_ptr & dialog); ///< creates a anonymous pipe and bind itself to the writing end. The reading end can be obtained by get_read_side() method tuyau(const tuyau & ref) = default; tuyau(tuyau && ref) noexcept = default; tuyau & operator = (const tuyau & ref) = default; tuyau & operator = (tuyau && ref) noexcept = default; ~tuyau(); /// provides the reading end of the anonymous pipe when the current object has created it (no filedesc, no path given to constructor). /// \note this methid cannot be called more than once. int get_read_fd() const; /// closes the read fd of the anonymous pipe (this is to be used by a writer) /// \note to ensure a proper behavior of the 'eof', the writer must close the read fd /// this call let this be done, assuming the read has already fetched the fd and forked /// in a new process void close_read_fd(); /// ask to not close the read descriptor upon object destruction (the fd survives the object) void do_not_close_read_fd(); // inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(signed int x) override; virtual bool truncatable(const infinint & pos) const override { return pos >= position; }; virtual infinint get_position() const override { return position; }; bool has_next_to_read(); protected: virtual void inherited_read_ahead(const infinint & amount) override {}; // relying on the operating system virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override { if(pos < position) throw SRC_BUG; }; virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override; private: enum ///< anonymous structure for pipe_mode field { pipe_fd, ///< holds a single file descriptor for the pipe pipe_path, ///< holds a filename to be openned (named pipe) pipe_both ///< holds a pair of file descriptors } pipe_mode; ///< defines how the object's status (which possible values defined by the anonymous enum above) infinint position; ///< recorded position in the stream int filedesc; ///< file descriptors of the pipe int other_end_fd; ///< in pipe_both mode, this holds the reading side of the anonymous pipe std::string chemin; ///< in pipe_path mode only, this holds the named pipe to be open bool has_one_to_read; ///< if true, the next char to read is placed in "next_to_read" char next_to_read; ///< when has_one_to_read is true, contains the next to read byte void ouverture(); /// skip forward by reading data /// \param[in] byte is the amount of byte to skip forward /// \return true if the given amount of byte could be read, false otherwise (reached EOF). bool read_and_drop(infinint byte); /// skip to eof by reading data bool read_to_eof(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_nomme.hpp0000644000175000017500000000530414636066467014020 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_nomme.hpp /// \brief base class of all objects contained in a catalogue and that can be named /// \ingroup Private #ifndef CAT_NOMME_HPP #define CAT_NOMME_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include #include "cat_entree.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the base class for all entry that have a name class cat_nomme : public cat_entree { public: cat_nomme(const std::string & name, saved_status arg): cat_entree(arg) { xname = name; }; cat_nomme(const smart_pointer & pdesc, bool small, saved_status val); cat_nomme(const cat_nomme & ref) = default; cat_nomme(cat_nomme && ref) noexcept = default; cat_nomme & operator = (const cat_nomme & ref) = default; cat_nomme & operator = (cat_nomme && ref) = default; virtual ~cat_nomme() = default; virtual bool operator == (const cat_entree & ref) const override; virtual bool operator < (const cat_nomme & ref) const { return xname < ref.xname; }; const std::string & get_name() const { return xname; }; void change_name(const std::string & x) { xname = x; }; /// compares two objects /// /// \note no need to have a virtual method, as signature will differ in inherited classes (argument type changes) bool same_as(const cat_nomme & ref) const { return cat_entree::same_as(ref) && xname == ref.xname; }; // signature() is kept as an abstract method // clone() is also ketp abstract protected: virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; private: std::string xname; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/hash_fichier.hpp0000644000175000017500000001242614636067146014470 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file hash_fichier.hpp /// \brief class hash_fichier definition. /// \ingroup Private /// /// This is an inherited class from class fichier /// Objects of that class are write-only objects that provide a hash of the written data /// other hash algorithm may be added in the future #ifndef HASH_FICHIER_HPP #define HASH_FICHIER_HPP #include "../my_config.h" extern "C" { #if HAVE_GCRYPT_H #ifndef GCRYPT_NO_DEPRECATED #define GCRYPT_NO_DEPRECATED #endif #include #endif } #include #include "fichier_global.hpp" #include "integers.hpp" #include "archive_aux.hpp" namespace libdar { /// \addtogroup Private /// @{ /// manages the generation of a hash class hash_fichier : public fichier_global { public: /// hash_file constructor /// /// \param[in] dialog for user interaction /// \param[in] under points to an object where to write data to /// \param[in] under_filename name of the plain file we write to, this argument is required to build the hash file /// \param[in] hash_file points to an object where to drop the hash file once writings are finished /// \param[in] algo hash algorithm to use. hash_none is not an acceptable value /// \note if the constructor succeed, the objects pointed to by under and hash_file are owned and deleted by this hash_file object hash_fichier(const std::shared_ptr & dialog, fichier_global *under, const std::string & under_filename, fichier_global *hash_file, hash_algo algo); /// copy constructor hash_fichier(const hash_fichier & ref) = delete; /// move constructor hash_fichier(hash_fichier && ref) noexcept = delete; /// assignment operator hash_fichier & operator = (const hash_fichier & ref) = delete; /// move operator hash_fichier & operator = (hash_fichier && ref) noexcept = delete; /// destructor ~hash_fichier(); // inherited from fichier_global virtual void change_ownership(const std::string & user, const std::string & group) override { if(ref == nullptr || hash_ref == nullptr) throw SRC_BUG; ref->change_ownership(user, group); hash_ref->change_ownership(user, group); }; virtual void change_permission(U_I perm) override { if(ref == nullptr || hash_ref == nullptr) throw SRC_BUG; ref->change_permission(perm); hash_ref->change_permission(perm); }; virtual infinint get_size() const override { if(ref == nullptr) throw SRC_BUG; return ref->get_size(); }; virtual void fadvise(advise adv) const override { if(ref == nullptr) throw SRC_BUG; ref->fadvise(adv); }; // inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override { return false; }; virtual bool skip(const infinint & pos) override {if(ref == nullptr || pos != ref->get_position()) throw SRC_BUG; else return true; }; virtual bool skip_to_eof() override { if(get_mode() == gf_write_only) return true; else throw SRC_BUG; }; virtual bool skip_relative(S_I x) override { if(x != 0) throw SRC_BUG; else return true; }; virtual bool truncatable(const infinint & pos) const override { return false; }; virtual infinint get_position() const override { if(ref == nullptr) throw SRC_BUG; return ref->get_position(); }; /// for debugging purposes only void set_only_hash() { only_hash = true; }; protected: // inherited from fichier_global virtual void inherited_read_ahead(const infinint & amount) override { ref->read_ahead(amount); }; virtual U_I fichier_global_inherited_write(const char *a, U_I size) override; virtual bool fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) override; // inherited from generic_file virtual void inherited_truncate(const infinint & pos) override { throw SRC_BUG; }; // truncate not supported on hash files virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override; private: fichier_global *ref; fichier_global *hash_ref; bool only_hash; ///< if set, avoids copying data to file, only compute hash (debugging purpose) #if CRYPTO_AVAILABLE gcry_md_hd_t hash_handle; #endif std::string ref_filename; U_I hash_gcrypt; bool eof; bool hash_dumped; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive_version.hpp0000644000175000017500000000633214636066467015246 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive_version.hpp /// \brief class archive_version that rules which archive format to follow /// \ingroup API #ifndef ARCHIVE_VERSION_HPP #define ARCHIVE_VERSION_HPP #include "../my_config.h" #include #include "integers.hpp" namespace libdar { /// \addtogroup API /// @{ // no need to dig into this from API class generic_file; /// class archive_version manages the version of the archive format class archive_version { public: /// archive_version constructor /// \param[in] x is the version number /// \param[in] fix is a decimal-like /// \note the fix argument must only be used when the current stable version need to be increased /// due to a bug fix. This let the concurrent development version to keep the same version (usually x+1) /// while having the stable version using a slightly different format to fix a bug. archive_version(U_16 x = 0, unsigned char fix = 0); archive_version(const archive_version & ref) = default; archive_version(archive_version && ref) noexcept = default; archive_version & operator = (const archive_version & ref) = default; archive_version & operator = (archive_version && ref) noexcept = default; ~archive_version() = default; bool operator < (const archive_version & ref) const { return value() < ref.value(); }; bool operator >= (const archive_version & ref) const { return value() >= ref.value(); }; bool operator == (const archive_version & ref) const { return value() == ref.value(); }; bool operator != (const archive_version & ref) const { return value() != ref.value(); }; bool operator > (const archive_version & ref) const { return value() > ref.value(); }; bool operator <= (const archive_version & ref) const { return value() <= ref.value(); }; void dump(generic_file & f) const; void read(generic_file & f); /// provides the version information as a human readable string std::string display() const; private: U_16 version; unsigned char fix; U_I value() const { return (U_I)(version)*256 + fix; }; static unsigned char to_digit(unsigned char val); static unsigned char to_char(unsigned char val); }; extern const archive_version empty_archive_version(); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/gzip_module.hpp0000644000175000017500000000451714636066467014401 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file gzip_module.hpp /// \brief per block encryption using gzip algorithm/library /// \ingroup Private /// #ifndef GZIP_MODULE_HPP #define GZIP_MODULE_HPP extern "C" { } #include "../my_config.h" #include "compress_module.hpp" namespace libdar { /// \addtogroup Private /// @{ class gzip_module: public compress_module { public: gzip_module(U_I compression_level = 9); gzip_module(const gzip_module & ref) = default; gzip_module(gzip_module && ref) noexcept = default; gzip_module & operator = (const gzip_module & ref) = default; gzip_module & operator = (gzip_module && ref) noexcept = default; virtual ~gzip_module() noexcept = default; // inherited from compress_module interface virtual compression get_algo() const override { return compression::gzip; }; virtual U_I get_max_compressing_size() const override; virtual U_I get_min_size_to_compress(U_I clear_size) const override; virtual U_I compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const override; virtual U_I uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const override; virtual std::unique_ptr clone() const override; private: U_I level; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/header_flags.cpp0000644000175000017500000000532614636066467014461 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end of extern "C" #include "header_flags.hpp" #include "erreurs.hpp" using namespace std; namespace libdar { void header_flags::set_bits(U_I bitfield) { if(has_an_lsb_set(bitfield)) throw SRC_BUG; bits |= bitfield; } void header_flags::unset_bits(U_I bitfield) { if(has_an_lsb_set(bitfield)) throw SRC_BUG; bits &= ~bitfield; } bool header_flags::is_set(U_I bitfield) const { if(has_an_lsb_set(bitfield)) throw SRC_BUG; return (bits & bitfield) == bitfield; } void header_flags::read(generic_file & f) { bits = 0; unsigned char a; do { if(f.read((char *)&a, 1) != 1) throw Erange("header_glags::read", gettext("Reached End of File while reading flag field")); if(((bits << 8) >> 8) == bits) { bits <<= 8; bits |= a & 0xFE; } else throw Erange("header_flags::read", gettext("tool large flag field for this implementation, either data corruption occured or you need to upgrade your software")); } while((a & 0x01) > 0); } void header_flags::dump(generic_file & f) const { U_I bit_size = 8; U_I bitfield = bits; unsigned char tmp; // placing as needed 0x01 bits to keep trace of the width of the bitfield while((bitfield >> bit_size) > 0) { bitfield |= (0x01 << bit_size); bit_size += 8; } // writing down the most significant bits first while(bit_size > 0) { bit_size -= 8; tmp = ((bitfield >> bit_size) & 0xFF); f.write((char *)(& tmp), 1); } } bool header_flags::has_an_lsb_set(U_I bitfield) { while(bitfield > 0) if((bitfield & 0x01) > 0) return true; else bitfield >>= 8; return false; } } // end of namespace dar-2.7.15/src/libdar/tlv_list.cpp0000644000175000017500000000372014636066467013711 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "tlv_list.hpp" using namespace std; namespace libdar { void tlv_list::dump(generic_file & f) const { infinint number = contents.size(); deque::iterator it = const_cast(this)->contents.begin(); deque::iterator fin = const_cast(this)->contents.end(); number.dump(f); while(it != fin) { it->dump(f); it++; } } tlv & tlv_list::operator[] (U_I item) const { tlv_list *me = const_cast(this); if(item > contents.size()) throw Erange("tlv_list::operator[]", "index out of range when accessing a tlv_list object"); if(me == nullptr) throw SRC_BUG; return me->contents[item]; } void tlv_list::init(generic_file & f) { infinint number; number.read(f); // read from file the number of tlv stored contents.clear(); // erase list contents while(!number.is_zero()) // read each tlv from file { contents.push_back(tlv(f)); number--; } } } // end of namespace dar-2.7.15/src/libdar/secu_string.hpp0000644000175000017500000001750014636066467014404 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file secu_string.hpp /// \brief this file contains the definition of secu_string class, a std::string like class but allocated in secure memory /// \ingroup API /// /// secure memory is a allocated memory that is never swapped out (wrote to disk) /// the implementation relies on gcrypt_malloc_secure() call (libgcrypt) /// rather than relying on mlock()/munlock() posix system call. /// as the need for secure string is for strong encryption, there is no much /// interest in re-inventing the wheel as the need is dependent on gcrypt availability #ifndef SECU_STRING_HPP #define SECU_STRING_HPP #include "../my_config.h" #include #include "integers.hpp" #include "erreurs.hpp" namespace libdar { /// \addtogroup API /// @{ /// class secu_string /// it manages the allocation/release of a given size block of memory /// the memory block is forbidden to be swapped (if is_strin_secured() is true) /// and is zeroed before being released class secu_string { public: /// to know if secure memory is available /// it is advised that the user program of class secu_string uses this call before using objects of that class /// and if returned false, advise the user that the necessary support for secure memory /// is not present, that any sensitive data may be swapped out under heavy memory load and thus /// may lead secure password to be wrote to disk in clear. static bool is_string_secured(); /// constructor 1 /// create the allocated string in secure memory /// \param[in] storage_size is the amount of secured memory to obtain when creating the object secu_string(U_I storage_size = 0) { init(storage_size); }; /// constructor 2 /// create the string from a pointer to a (secure) string or from a portion of it secu_string(const char *ptr, U_I size) { init(size); append_at(0, ptr, size); }; /// the copy constructor secu_string(const secu_string & ref) { copy_from(ref); }; /// the move constructor secu_string(secu_string && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; /// the assignment operator secu_string & operator = (const secu_string & ref) { clean_and_destroy(); copy_from(ref); return *this; }; /// the move operator secu_string & operator = (secu_string && ref) noexcept { move_from(std::move(ref)); return *this; }; /// the destructor (set memory to zero before releasing it) ~secu_string() noexcept { clean_and_destroy(); }; bool operator != (const std::string & ref) const { return ! (*this == ref); }; bool operator != (const secu_string & ref) const { return ! (*this == ref); }; bool operator == (const std::string &ref) const { return compare_with(ref.c_str(),(U_I)(ref.size())); }; bool operator == (const secu_string &ref) const { return compare_with(ref.mem, *(ref.string_size)); }; /// fill the object with data /// set at most size bytes of data directly from the filedescriptor, /// \param[in] fd the filedescriptor to read data from /// \param[in] size is the maximum number of byte read /// \note if current storage size is not larg enough to hold size bytes, /// allocated secure memory is released and larger allocation of secure memory is done. void set(int fd, U_I size); /// append some data to the string at a given offset /// \param[in] offset defines at which offset in the secu_string will be placed the string to append /// \param[in] ptr is the address of the string to append /// \param[in] size is the number of byte to append /// \note this call does not change the allocation size, (unlike set()), it adds the data pointed by the arguments /// to the object while there is enough place to do so. /// resize() must be used first to define enough secure memory to append the expected amount of data /// in one or several call to append. void append_at(U_I offset, const char *ptr, U_I size); /// append some data to the string void append_at(U_I offset, int fd, U_I size); /// append some data at the end of the string void append(const char *ptr, U_I size) { append_at(*string_size, ptr, size); }; /// append some data at the end of the string void append(int fd, U_I size) { append_at(*string_size, fd, size); }; /// shorten the string (do not change the allocated size) /// /// \param[in] pos is the length of the string to set, it must be smaller or equal to the current size void reduce_string_size_to(U_I pos); /// set the string size within the allocated secure memory void expand_string_size_to(U_I size); /// clear the string (set to an empty string) void clear() { *string_size = 0; }; /// clear and resize the string to the defined allocated size /// \param[in] size is the amount of secure memory to allocated void resize(U_I size) { clean_and_destroy(); init(size); }; /// set the string to randomize string of given size /// \note the given size must be less than allocated size void randomize(U_I size); /// get access to the secure string /// \return the address of the first byte of the string /// \note check the "size" method to know how much bytes can be read const char* c_str() const { return mem == nullptr ? throw SRC_BUG : mem; }; char* c_str() { return mem == nullptr ? throw SRC_BUG : mem; }; void set_size(U_I size); /// non constant flavor of direct secure memory access char * get_array() { return mem == nullptr ? throw SRC_BUG : mem; }; /// get access to the secure string by index /// \note index must be in the range [ 0 - size() [ to avoid throwing an exception char & operator[] (U_I index); char operator[](U_I index) const { return (const_cast(this))->operator[](index); }; /// get the size of the string U_I get_size() const { if(string_size == nullptr) throw SRC_BUG; return *string_size; }; // returns the size of the string /// tell whether string is empty bool empty() const { if(string_size == nullptr) throw SRC_BUG; return *string_size == 0; }; /// get the size of the allocated secure space U_I get_allocated_size() const { return *allocated_size - 1; }; private: U_I *allocated_size; char *mem; U_I *string_size; void nullifyptr() noexcept { allocated_size = string_size = nullptr; mem = nullptr; }; void init(U_I size); //< to be used at creation time or after clean_and_destroy() only void copy_from(const secu_string & ref); //< to be used at creation time or after clean_and_destroy() only void move_from(secu_string && ref) noexcept { std::swap(allocated_size, ref.allocated_size); std::swap(mem, ref.mem); std::swap(string_size, ref.string_size); }; bool compare_with(const char *ptr, U_I size) const; // return true if given sequence is the same as the one stored in "this" void clean_and_destroy(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_ignored.hpp0000644000175000017500000000472514636066467014342 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_ignored.hpp /// \brief class used to remember that an entry has been ignored and shall not be recorded as deleted using a detruit object in a catalogue /// \ingroup Private #ifndef CAT_IGNORED_HPP #define CAT_IGNORED_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_nomme.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the present file to ignore (not to be recorded as deleted later) class cat_ignored : public cat_nomme { public : cat_ignored(const std::string & name): cat_nomme(name, saved_status::saved) {}; cat_ignored(const smart_pointer & pdesc, bool small) : cat_nomme(pdesc, small, saved_status::saved) { throw SRC_BUG; }; cat_ignored(const cat_ignored & ref) = default; cat_ignored(cat_ignored && ref) noexcept = default; cat_ignored & operator = (const cat_ignored & ref) = default; cat_ignored & operator = (cat_ignored && ref) = default; ~cat_ignored() = default; virtual bool operator == (const cat_entree & ref) const override; virtual unsigned char signature() const override { return 'i'; }; virtual std::string get_description() const override { return "ignored entry"; }; virtual cat_entree *clone() const override { return new (std::nothrow) cat_ignored(*this); }; protected: virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override { throw SRC_BUG; }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/deci.cpp0000644000175000017500000001627514636066467012766 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include #include "deci.hpp" #include "erreurs.hpp" #include "integers.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { using chiffre = unsigned char; static const U_I PAS = 5; static inline chiffre get_left(unsigned char a) { return (a & 0xF0) >> 4; } static inline chiffre get_right(unsigned char a) { return a & 0x0F; } static inline void set_left(unsigned char & a, chiffre val) { val <<= 4; a &= 0x0F; a |= val; } static inline void set_right(unsigned char & a, chiffre val) { val &= 0x0F; a &= 0xF0; a |= val; } static inline chiffre digit_htoc(unsigned char c) { if(c < '0' || c > '9') throw Edeci("deci.cpp : digit_htoc", gettext("invalid decimal digit")); return chiffre(c - '0'); } static inline unsigned char digit_ctoh(chiffre c) { if(c > 9) throw SRC_BUG; return '0' + c; } template void decicoupe(storage * &decimales, T x) { NLS_SWAP_IN; decimales = nullptr; try { chiffre r; const T d_t = 10; T r_t; storage::iterator it; bool recule = false; unsigned char tmp = 0; decimales = new (nothrow) storage(PAS); if(decimales == nullptr) throw Ememory("template deci::decicoupe"); decimales->clear(0xFF); it = decimales->rbegin(); while(x > 0 || recule) { if(x > 0) { euclide(x,d_t,x,r_t); r = 0; r_t.unstack(r); } else r = 0xF; // not significative information if(recule) { set_left(tmp, chiffre(r)); if(it == decimales->rend()) { decimales->insert_const_bytes_at_iterator(decimales->begin(), 0xFF, PAS); it = decimales->begin() + PAS - 1; } *(it--) = tmp; } else set_right(tmp, chiffre(r)); recule = ! recule; } } catch(...) { if(decimales != nullptr) { delete decimales; decimales = nullptr; } NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } deci::deci(string s) { NLS_SWAP_IN; decimales = nullptr; try { string::reverse_iterator it = s.rbegin(); storage::iterator ut; bool recule = false; unsigned char tmp = 0xFF; U_I size = s.size() / 2; if(s.size() % 2 != 0) size++; if(size == 0) // empty string throw Erange("deci::deci(string s)", gettext("an empty string is an invalid argument")); decimales = new (nothrow) storage(size); if(decimales == nullptr) throw Ememory("deci::deci(string s)"); decimales->clear(0xFF); // FF is not a valid couple of decimal digit ut = decimales->rbegin(); while(it != s.rend() || recule) { if(recule) { if(it != s.rend()) set_left(tmp, digit_htoc(*it)); else set_left(tmp, 0xF); if(ut == decimales->rend()) throw SRC_BUG; *(ut--) = tmp; } else set_right(tmp, digit_htoc(*it)); recule = ! recule; if(it != s.rend()) it++; // it is a reverse iterator thus ++ for going backward } reduce(); } catch(...) { if(decimales != nullptr) delete decimales; NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } deci::deci(const infinint & x) { try { decicoupe(decimales, x); reduce(); } catch(...) { if(decimales != nullptr) delete decimales; throw; } } void deci::copy_from(const deci & ref) { if(decimales != nullptr) throw SRC_BUG; decimales = new (nothrow) storage(*ref.decimales); if(decimales == nullptr) throw SRC_BUG; } void deci::detruit() { if(decimales != nullptr) { delete decimales; decimales = nullptr; } } void deci::reduce() { bool avance = false, leading_zero = true; chiffre tmp; infinint justif_size = 0; if(decimales == nullptr) throw SRC_BUG; storage::iterator it = decimales->begin(); while(it != decimales->end() && leading_zero) { if(avance) tmp = get_right(*it); else tmp = get_left(*it); if(tmp == 0 && leading_zero) { if(avance) set_right(*it, 0xF); else set_left(*it, 0xF); tmp = 0xF; } if(tmp == 0xF) { if(leading_zero) { if(avance) ++justif_size; } else throw SRC_BUG; } if(tmp != 0 && tmp != 0xF) leading_zero = false; if(avance) ++it; avance = ! avance; } if(justif_size == decimales->size()) { --justif_size; it = decimales->rbegin(); *it = 0xF0; // need at least one digit } if(justif_size > 0) decimales->remove_bytes_at_iterator(decimales->begin(), justif_size); } string deci::human() const { string s = ""; storage::iterator it = decimales->begin(), fin = decimales->end(); bool avance = false; chiffre c; while(it != fin) { if(avance) { c = get_right(*it); ++it; } else c = get_left(*it); if(c != 0xF) s = s + string(1, digit_ctoh(c)); avance = ! avance; } return s; } infinint deci::computer() const { infinint r = 0; storage::iterator it = decimales->begin(), fin = decimales->end(); bool avance = false; chiffre c; while(it != fin) { if(avance) { c = get_right(*it); ++it; } else c = get_left(*it); if(c != 0xF) { r *= 10; r += c; } avance = ! avance; } return r; } ostream & operator << (ostream & ref, const infinint & arg) { deci tmp = arg; ref << tmp.human(); return ref; } } // end of namespace dar-2.7.15/src/libdar/ea.cpp0000644000175000017500000001217514636066467012442 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "ea.hpp" #include "tools.hpp" #include "integers.hpp" #include "infinint.hpp" #include "archive_version.hpp" // these MACRO are used only when dumping in file (obsolete since archive format "05") #define EA_ROOT 0x80 #define EA_DEL 0x40 #define EA_DEFAULT_USER_INSERT 0x00 using namespace std; namespace libdar { ///////////// STATIC FUNCTION DECLARATION /////////// static void read_pair_string(generic_file & f, const archive_version & edit, string & key, string & val); static void write_pair_key(generic_file & f, const string & key, const string & val); ///////////// EA_ATTRIBUTS IMPLEMENTATION ////////// ea_attributs::ea_attributs(generic_file & f, const archive_version & edit) { infinint tmp = infinint(f); // number of EA string key, val; U_32 tmp2 = 0; tmp.unstack(tmp2); do { while(tmp2 > 0) { read_pair_string(f, edit, key, val); attr[key] = val; tmp2--; } tmp.unstack(tmp2); } while(tmp2 > 0); alire = attr.begin(); } ea_attributs::ea_attributs(const ea_attributs & ref) { attr = ref.attr; alire = attr.begin(); } ea_attributs::ea_attributs(ea_attributs && ref) noexcept { attr = move(ref.attr); alire = attr.begin(); } ea_attributs & ea_attributs::operator = (const ea_attributs & ref) { attr = ref.attr; alire = attr.begin(); return *this; } ea_attributs & ea_attributs::operator = (ea_attributs && ref) noexcept { attr = move(ref.attr); alire = attr.begin(); return *this; } void ea_attributs::dump(generic_file & f) const { map::const_iterator it = attr.begin(); size().dump(f); while(it != attr.end()) { write_pair_key(f, it->first, it->second); it++; } } void ea_attributs::reset_read() const { alire = attr.begin(); } bool ea_attributs::read(string & key, string & value) const { if(alire != attr.end()) { key = alire->first; value = alire->second; ++alire; return true; } else return false; } bool ea_attributs::diff(const ea_attributs & other, const mask & filter) const { string key; string val; string value; bool diff = false; reset_read(); while(!diff && read(key, val)) if(filter.is_covered(key)) { if(!other.find(key, value) || value != val) // not found or different diff = true; } return diff; } bool ea_attributs::find(const string & key, string & found_value) const { map::const_iterator it = attr.find(key); if(it != attr.end()) { found_value = it->second; if(it->first != key) throw SRC_BUG; return true; } else return false; } infinint ea_attributs::space_used() const { map::const_iterator it = attr.begin(); infinint ret = 0; while(it != attr.end()) { ret += it->first.size() + it->second.size(); ++it; } return ret; } ea_attributs ea_attributs::operator + (const ea_attributs & arg) const { ea_attributs ret = *this; // copy constructor string key, value; arg.reset_read(); while(arg.read(key, value)) ret.add(key, value); return ret; } ///////////// STATIC FUNCTION IMPLEMENTATION /////// static void read_pair_string(generic_file & f, const archive_version & edit, string & key, string & val) { infinint tmp; unsigned char fl; string pre_key = ""; if(edit < 5) // old format { f.read((char *)(&fl), 1); if((fl & EA_ROOT) != 0) pre_key = "system."; else pre_key = "user."; } tools_read_string(f, key); key = pre_key + key; tmp = infinint(f); tools_read_string_size(f, val, tmp); } static void write_pair_key(generic_file & f, const string & key, const string & val) { infinint tmp = val.size(); tools_write_string(f, key); tmp.dump(f); tools_write_string_all(f, val); } } // end of namespace dar-2.7.15/src/libdar/shell_interaction.hpp0000644000175000017500000001511714636067146015562 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file shell_interaction.hpp /// \brief user_interaction class used by default /// \ingroup API #ifndef SHELL_INTERACTION_HPP #define SHELL_INTERACTION_HPP extern "C" { #if HAVE_TERMIOS_H #include #endif } // end extern "C" #include "../my_config.h" #include #include "user_interaction.hpp" #include "database.hpp" #include "archive_options_listing_shell.hpp" namespace libdar { /// \addtogroup CMDLINE /// @{ class shell_interaction : public user_interaction { public: /// constructor /// /// \param[in] out defines where are sent non interactive messages (informative messages) /// \param[in] interact defines where are sent interactive messages (those requiring an answer or confirmation) from the user /// \param[in] silent whether to send a warning message if the process is not attached to a terminal shell_interaction(std::ostream & out, std::ostream & interact, bool silent); /// copy constructor shell_interaction(const shell_interaction & ref); /// no move constructor shell_interaction(shell_interaction && ref) noexcept = delete; /// assignment operator (not implmented) shell_interaction & operator = (const shell_interaction & ref) = delete; /// no move operator shell_interaction & operator = (shell_interaction && ref) noexcept = delete; /// destructor ~shell_interaction(); void change_non_interactive_output(std::ostream & out); void read_char(char & a); void set_beep(bool mode) { beep = mode; }; /// make a pause each N line of output when calling the warning method /// \param[in] num is the number of line to display at once, zero for unlimited display /// \note. Since API 3.1, the warning method is no more a pure virtual function /// you need to call the parent warning method in your method for this warning_with_more /// method works as expected. void warning_with_more(U_I num) { at_once = num; count = 0; }; /// display an archive content void archive_show_contents(const archive & ref, const archive_options_listing_shell & options); /// show database contents void database_show_contents(const database & ref); /// show files of a given archive in database void database_show_files(const database & ref, archive_num num, const database_used_options & opt); /// show versions of a given file in database void database_show_version(const database & ref, const path & chem); /// show per archive statistics of the database void database_show_statistics(const database &ref); protected: // inherited methods from user_interaction class virtual void inherited_message(const std::string & message) override; virtual bool inherited_pause(const std::string &message) override; virtual std::string inherited_get_string(const std::string & message, bool echo) override; virtual secu_string inherited_get_secu_string(const std::string & message, bool echo) override; private: // data type enum mode { m_initial, m_inter, m_noecho }; // object fields and methods S_I input; ///< filedescriptor to read from the user's answers std::ostream *output; ///< holds the destination for non interactive messages std::ostream *inter; ///< holds the destination for interactive messages bool beep; ///< whether to issue bell char before displaying a new interactive message termios initial; ///< controlling terminal configuration when the object has been created termios interaction; ///< controlling terminal configuration to use when requiring user interaction termios initial_noecho; ///< controlling terminal configuration to use when noecho has been requested bool has_terminal; ///< true if a terminal could be found U_I at_once, count; ///< used by warning_with_more // field used by listing_callbacks bool archive_listing_sizes_in_bytes; ///< to be used by listing_callbacks bool archive_listing_display_ea; ///< to be used by listing_callbacks range all_slices; ///< all the slices covered by the slicing listing operation std::string marge; ///< used for the tree and XML listing operation void set_term_mod(mode m); void my_message(const std::string & mesg); void xml_listing_attributes(const list_entry & entry); // class fields and methods static const U_I bufsize; static constexpr const char* REMOVE_TAG = "[--- REMOVED ENTRY ----]"; static void archive_listing_callback_tree(const std::string & the_path, const list_entry & entry, void *context); static void archive_listing_callback_tar(const std::string & the_path, const list_entry & entry, void *context); static void archive_listing_callback_xml(const std::string & the_path, const list_entry & entry, void *context); static void archive_listing_callback_slicing(const std::string & the_path, const list_entry & entry, void *context); static void show_files_callback(void *tag, const std::string & filename, bool available_data, bool available_ea); static void get_version_callback(void *tag, archive_num num, db_etat data_presence, bool has_data_date, datetime data, db_etat ea_presence, bool has_ea_date, datetime ea); static void statistics_callback(void *tag, U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea); static std::string yes_no(bool val) { return (val ? "yes" : "no"); } }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/database_header.cpp0000644000175000017500000001524014636066467015125 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_STRING_H #include #endif } // end extern "C" #include "database_header.hpp" #include "tools.hpp" #include "user_interaction.hpp" #include "integers.hpp" #include "cygwin_adapt.hpp" #include "fichier_local.hpp" #include "compressor.hpp" #include "pile.hpp" #include "macro_tools.hpp" using namespace std; namespace libdar { static const unsigned char database_version = 6; #define HEADER_OPTION_NONE 0x00 #define HEADER_OPTION_COMPRESSOR 0x01 #define HEADER_OPTION_EXTENSION 0x80 // if EXTENSION bit is set, the option field is two bytes wide // this mechanism can be extended in the future by a second extension bit 0x8080 // and so on class database_header { public: database_header() { version = database_version; options = HEADER_OPTION_NONE; algo = compression::gzip; compression_level = 9; }; database_header(const database_header & ref) = default; database_header(database_header && ref) noexcept = default; database_header & operator = (const database_header & ref) = default; database_header & operator = (database_header && ref) noexcept = default; ~database_header() = default; void read(generic_file & f); void write(generic_file & f); void set_compression(compression algozip, U_I level); U_I get_version() const { return version; }; compression get_compression() const { return algo; }; U_I get_compression_level() const { return compression_level; }; private: unsigned char version; unsigned char options; compression algo; U_I compression_level; }; void database_header::read(generic_file & f) { f.read((char *)&version, 1); if(version > database_version) throw Erange("database_header::read", gettext("The format version of this database is too high for that software version, use a more recent software to read or modify this database")); f.read((char *)&options, 1); if((options & HEADER_OPTION_EXTENSION) != 0) throw Erange("database_header::read", gettext("Unknown header option in database, aborting\n")); if((options & HEADER_OPTION_COMPRESSOR) != 0) { char tmp; f.read(&tmp, 1); algo = char2compression(tmp); if(version > 5) // must read the compression level too { infinint tmp(f); // reading an infinint from f compression_level = 0; tmp.unstack(compression_level); } } else { algo = compression::gzip; // was the default before choice was available compression_level = 9; } } void database_header::write(generic_file & f) { f.write((char *)&version, 1); f.write((char *)&options, 1); if((options & HEADER_OPTION_COMPRESSOR) != 0) { char tmp = compression2char(algo); f.write(&tmp, 1); infinint(compression_level).dump(f); } } void database_header::set_compression(compression algozip, U_I level) { algo = algozip; compression_level = level; if(algo != compression::gzip || level != 9) options |= HEADER_OPTION_COMPRESSOR; else options &= ~HEADER_OPTION_COMPRESSOR; } generic_file *database_header_create(const shared_ptr & dialog, const string & filename, bool overwrite, compression algozip, U_I compr_level) { pile* stack = new (nothrow) pile(); generic_file *tmp = nullptr; struct stat buf; database_header h; proto_compressor *comp; if(stack == nullptr) throw Ememory("database_header_create"); try { if(stat(filename.c_str(), &buf) >= 0 && !overwrite) throw Erange("database_header_create", gettext("Cannot create database, file exists")); tmp = new (nothrow) fichier_local(dialog, filename, gf_write_only, 0666, !overwrite, overwrite, false); if(tmp == nullptr) throw Ememory("database_header_create"); stack->push(tmp); // now the fichier_local is managed by stack h.set_compression(algozip, compr_level); h.write(*stack); comp = macro_tools_build_streaming_compressor(algozip, *(stack->top()), compr_level, 2); // using 2 workers at most if(comp == nullptr) throw Ememory("database_header_create"); stack->push(comp); } catch(...) { delete stack; throw; } return stack; } generic_file *database_header_open(const shared_ptr & dialog, const string & filename, unsigned char & db_version, compression & algozip, U_I & compr_level) { pile *stack = new (nothrow) pile(); generic_file *tmp = nullptr; if(stack == nullptr) throw Ememory("database_header_open"); try { database_header h; try { tmp = new (nothrow) fichier_local(filename, false); } catch(Erange & e) { throw Erange("database_header_open", tools_printf(gettext("Error reading database %S : "), &filename) + e.get_message()); } if(tmp == nullptr) throw Ememory("database_header_open"); stack->push(tmp); h.read(*stack); db_version = h.get_version(); algozip = h.get_compression(); compr_level = h.get_compression_level(); tmp = macro_tools_build_streaming_compressor(algozip, *(stack->top()), compr_level, // not used for decompression (here) 2); // using 2 workers at most if(tmp == nullptr) throw Ememory("database_header_open"); stack->push(tmp); } catch(...) { delete stack; throw; } return stack; } extern const unsigned char database_header_get_supported_version() { return database_version; } } // end of namespace dar-2.7.15/src/libdar/archive5.hpp0000644000175000017500000001227714636067146013566 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive5.hpp /// \brief API v5 backward compatible class archive /// \ingroup API5 #ifndef ARCHIVE5_HPP #define ARCHIVE5_HPP #include "../my_config.h" #include "archive.hpp" #include "user_interaction5.hpp" #include "archive_options5.hpp" namespace libdar5 { /// \addtogroup API5 /// @{ // from archive_options.hpp using libdar::path; using libdar::statistics; /// the archive class realizes the most general operations on archives /// the operations corresponds to the one the final user expects, these /// are the same abstraction level as the operation realized by the DAR /// command line tool. /// \ingroup API5 class archive: public libdar::archive { public: archive(user_interaction & dialog, const path & chem, const std::string & basename, const std::string & extension, const archive_options_read & options): libdar::archive(user_interaction5_clone_to_shared_ptr(dialog), chem, basename, extension, options) {} archive(user_interaction & dialog, const path & fs_root, const path & sauv_path, const std::string & filename, const std::string & extension, const archive_options_create & options, statistics * progressive_report): libdar::archive(user_interaction5_clone_to_shared_ptr(dialog), fs_root, sauv_path, filename, extension, options, progressive_report) {} archive(user_interaction & dialog, const path & sauv_path, archive *ref_arch1, const std::string & filename, const std::string & extension, const archive_options_merge & options, statistics * progressive_report): libdar::archive(user_interaction5_clone_to_shared_ptr(dialog), sauv_path, std::shared_ptr(ref_arch1), filename, extension, options, progressive_report) {} archive(user_interaction & dialog, const path & chem_src, const std::string & basename_src, const std::string & extension_src, const archive_options_read & options_read, const path & chem_dst, const std::string & basename_dst, const std::string & extension_dst, const archive_options_repair & options_repair): libdar::archive(user_interaction5_clone_to_shared_ptr(dialog), chem_src, basename_src, extension_src, options_read, chem_dst, basename_dst, extension_dst, options_repair) {} statistics op_extract(user_interaction & dialog, const path &fs_root, const archive_options_extract & options, statistics *progressive_report) { return libdar::archive::op_extract(fs_root, options, progressive_report); } void summary(user_interaction & dialog) { libdar::archive::summary(); } /// overwriting op_listing to use the user_interaction as callback void op_listing(user_interaction & dialog, const archive_options_listing & options); statistics op_diff(user_interaction & dialog, const path & fs_root, const archive_options_diff & options, statistics * progressive_report) { return libdar::archive::op_diff(fs_root, options, progressive_report); } statistics op_test(user_interaction & dialog, const archive_options_test & options, statistics * progressive_report) { return libdar::archive::op_test(options, progressive_report); } void op_isolate(user_interaction & dialog, const path &sauv_path, const std::string & filename, const std::string & extension, const archive_options_isolate & options) { libdar::archive::op_isolate(sauv_path, filename, extension, options); } /// overloading get_children_of to use the user_interaction object as callback bool get_children_of(user_interaction & dialog, const std::string & dir); void init_catalogue(user_interaction & dialog) const { libdar::archive::init_catalogue(); } void drop_all_filedescriptors(user_interaction & dialog) { libdar::archive::drop_all_filedescriptors(); } private: static void listing_callback(const std::string & the_path, const libdar::list_entry & entry, void *context); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/zapette.hpp0000644000175000017500000001417714636066467013542 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file zapette.hpp /// \brief remote control between dar and dar_slave. /// \ingroup Private /// /// Two objects communicate through a paire of pipes: /// - zapette is the dar side master class /// - slave_zapette dar_slave side /// . /// they form a communication channel between dar/libdar (zapette side) /// and dar_slave/libdar (dar_slave side) #ifndef ZAPETTE_HPP #define ZAPETTE_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" #include "integers.hpp" #include "mem_ui.hpp" #include "contextual.hpp" namespace libdar { /// \addtogroup Private /// @{ /// zapette emulate a file that is remotely controlled by slave_zapette /// class zapette sends order to slave_zapette through a /// a first pipe and receive informations or data in return /// from a second pipe from slave_zapette class zapette : public generic_file, public contextual, protected mem_ui { public: /// zapette constructor /// \param[in] dialog is used to return status information to the user /// \param[in] input is the pipe (see class tuyau) from which is received the information or data /// \param[in] output is used to send orders to slave_zapette /// \param[in] by_the_end if true dar will try to open the archive starting from the end else it will try starting from the first bytes zapette(const std::shared_ptr & dialog, generic_file *input, generic_file *output, bool by_the_end); zapette(const zapette & ref) = default; zapette(zapette && ref) noexcept = default; zapette & operator = (const zapette & ref) = default; zapette & operator = (zapette && ref) noexcept = default; ~zapette(); // inherited methods from generic_file virtual bool skippable(skippability direction, const infinint & amount) override { return true; }; virtual bool skip(const infinint &pos) override; virtual bool skip_to_eof() override { if(is_terminated()) throw SRC_BUG; position = file_size; return true; }; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return false; }; virtual infinint get_position() const override { if(is_terminated()) throw SRC_BUG; return position; }; // overwritten inherited methods from contextual virtual void set_info_status(const std::string & s) override; virtual bool is_an_old_start_end_archive() const override; virtual const label & get_data_name() const override; /// get the first slice header /// /// \note may return 0 if the slice header is not known infinint get_first_slice_header_size() const; /// get the non first slice header /// /// \note may return 0 if the slice header is not known infinint get_non_first_slice_header_size() const; protected: virtual void inherited_read_ahead(const infinint & amount) override {}; // optimization will be done when zapette will use the messaging_encode/decode exchange format virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override { throw SRC_BUG; }; // read only object virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override; private: generic_file *in, *out; infinint position, file_size; char serial_counter; /// wrapped formatted method to communicate with the slave_zapette located behind the pair of pipes (= tuyau) /// \param[in] size is the size of the amount of data we want the zapette to send us /// \param[in] offset is the byte offset of the portion of the data we want /// \param[in,out] data is the location where to return the requested data /// \param[in] info the new contextual string to set to the slave_zapette. /// \param[out] lu the amount of byte wrote to '*data' /// \param[out] arg infinint value return for special order (see note below). /// \note with default parameters, this method permits the caller to get a portion of data from the /// remote slave_zapette. In addition, it let the caller change the 'contextual' status of the remote object. /// if size is set to REQUEST_SPECIAL_ORDER, the offset is used to transmit a special order to the /// remote slave_zapette. Defined order are for example REQUEST_OFFSET_END_TRANSMIT , REQUEST_OFFSET_GET_FILESIZE, /// and so on (see at the beginning of zapette.cpp file for more). Each of these order may expect a returned value /// which may be an integer (provided by the "arg" argument of this call) a boolean value (provided by the "arg" /// argument where 0 means false and 1 means true) or a char * (first byte to put the answer to is given by 'data' and /// allocated space for the reply must be given through 'lu' which at return gives the effective length of the returned /// string void make_transfert(U_16 size, const infinint &offset, char *data, const std::string & info, S_I & lu, infinint & arg) const; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mycurl_easyhandle_node.hpp0000644000175000017500000001405214636067146016566 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mycurl_easyhandle_node.hpp /// \brief used to optimize session creation done by libcurl /// \ingroup Private #ifndef MYCURL_EASYHANDLE_NODE_HPP #define MYCURL_EASYHANDLE_NODE_HPP #include "../my_config.h" extern "C" { #if LIBCURL_AVAILABLE #if HAVE_CURL_CURL_H #include #endif #endif } // end extern "C" #include #include #include "mycurl_param_list.hpp" #include "user_interaction.hpp" #include "mycurl_slist.hpp" #include "tools.hpp" namespace libdar { /// \addtogroup Private /// @{ #if LIBCURL_AVAILABLE /// structure managing libcurl CURL* easy_handle class mycurl_easyhandle_node { public: /// create a new easyhandle mycurl_easyhandle_node() { init(); }; /// copy constructor mycurl_easyhandle_node(const mycurl_easyhandle_node & ref); /// move constructor mycurl_easyhandle_node(mycurl_easyhandle_node && ref) noexcept; /// assignment operator mycurl_easyhandle_node & operator = (const mycurl_easyhandle_node & ref); /// move operator mycurl_easyhandle_node & operator = (mycurl_easyhandle_node && ref) noexcept; /// destructor ~mycurl_easyhandle_node() { if(handle != nullptr) curl_easy_cleanup(handle); }; /// set options template void setopt(CURLoption opt, const T & val) { check_for_type(opt, val); wanted.add(opt, val); } /// adds a set of options void setopt_list(const mycurl_param_list & listing) { (void) wanted.update_with(listing); }; /// set back to default void setopt_default(CURLoption opt); /// set back all options to their default void setopt_all_default(); /// apply changed options since last call to apply, then execute curl_perform() void apply(const std::shared_ptr & dialog, U_I wait_seconds, const bool & end_anyway = false); /// get informations on the previous apply template void getinfo(CURLINFO info, T* val) { CURLcode err = curl_easy_getinfo(handle, info, val); if(err != CURLE_OK) throw Erange("mycurl_easyhandle_node::getinfo", tools_printf(gettext("Error met while fetching info %d: %s"), (S_I)info, curl_easy_strerror(err))); } static void init_defaults(); // must be called once libgcrypt has been initialized (due to secu_string presence in the defaults) static void release_defaults() { defaults.clear(); }; // must be called before libgcrypt is cleaned up private: //////////////////////////////////// // object level fields and methods // enum opttype { type_string, type_secu_string, type_pointer, type_long, type_mycurl_slist, type_curl_off_t, eolist ///< this is not a type just for flagging the end of a list }; CURL *handle; mycurl_param_list current; mycurl_param_list wanted; void init(); templatevoid set_to_default(CURLoption opt) { const T* ptr; if(current.get_val(opt, ptr)) { if(defaults.get_val(opt, ptr)) wanted.add(opt, *ptr); else throw SRC_BUG; } else wanted.clear(opt); } //////////////////////////////////// // class level fields and methods // struct opt_asso { CURLoption opt; opttype cast; }; static constexpr const opt_asso association[] = { { CURLOPT_APPEND, type_long }, { CURLOPT_DIRLISTONLY, type_long }, { CURLOPT_NETRC, type_long }, { CURLOPT_NOBODY, type_long }, { CURLOPT_SSH_KNOWNHOSTS, type_string }, { CURLOPT_SSH_PUBLIC_KEYFILE, type_string }, { CURLOPT_SSH_PRIVATE_KEYFILE, type_string }, { CURLOPT_SSH_AUTH_TYPES, type_long }, { CURLOPT_QUOTE, type_mycurl_slist }, { CURLOPT_RANGE, type_string }, { CURLOPT_READDATA, type_pointer }, { CURLOPT_READFUNCTION, type_pointer }, { CURLOPT_RESUME_FROM_LARGE, type_curl_off_t }, { CURLOPT_UPLOAD, type_long }, { CURLOPT_URL, type_string }, { CURLOPT_USERNAME, type_string }, { CURLOPT_USERPWD, type_secu_string }, { CURLOPT_VERBOSE, type_long }, { CURLOPT_WRITEDATA, type_pointer }, { CURLOPT_WRITEFUNCTION, type_pointer }, // eolist is needed to flag the end of list, option type does not matter { CURLOPT_APPEND, eolist } }; static opttype get_opt_type(CURLoption opt); static bool defaults_initialized; static mycurl_param_list defaults; template void check_for_type(CURLoption opt, const T & val) { switch(get_opt_type(opt)) { case type_string: if(typeid(val) != typeid(std::string)) throw SRC_BUG; break; case type_secu_string: if(typeid(val) != typeid(secu_string)) throw SRC_BUG; break; case type_pointer: if(typeid(val) != typeid(void *)) throw SRC_BUG; break; case type_long: if(typeid(val) != typeid(long)) throw SRC_BUG; break; case type_mycurl_slist: if(typeid(val) != typeid(mycurl_slist)) throw SRC_BUG; break; case type_curl_off_t: if(typeid(val) != typeid(curl_off_t)) throw SRC_BUG; break; case eolist: throw SRC_BUG; default: throw SRC_BUG; } } }; #endif /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cygwin_adapt.hpp0000644000175000017500000000245414636066467014532 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cygwin_adapt.hpp /// \brief thin C++ adaptation layer to Cygwin specifities /// \ingroup Private #ifndef CYGWIN_ADAPT_HPP #define CYGWIN_ADAPT_HPP #include "../my_config.h" /// \addtogroup Private /// @{ extern "C" { #include "cygwin_adapt.h" } // end extern "C" /// @} #endif dar-2.7.15/src/libdar/elastic.hpp0000644000175000017500000000550114636066467013501 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file elastic.hpp /// \brief here is defined the elastic class /// \ingroup Private #ifndef ELASTIC_HPP #define ELASTIC_HPP #include "../my_config.h" #include "integers.hpp" #include "infinint.hpp" #include "generic_file.hpp" #include "archive_version.hpp" namespace libdar { /// \addtogroup Private /// @{ /// reading direction of an elastic buffer enum elastic_direction { elastic_forward, elastic_backward }; /// the elastic buffer class /// the elastic class makes possible to insert arbritrary bytes beside information bytes, and to /// retreive later without any other knowledge which bytes are information and which byte are from the /// elastic buffer. The main purpose is for strong encryption class elastic { public: elastic(U_32 size); elastic(const unsigned char *buffer, U_32 size, elastic_direction dir, const archive_version & reading_ver); elastic(generic_file &f, elastic_direction dir, const archive_version & reading_ver); elastic(const elastic & ref) = default; elastic(elastic && ref) noexcept = default; elastic & operator = (const elastic & ref) = default; elastic & operator = (elastic && ref) noexcept = default; ~elastic() = default; U_32 dump(unsigned char *buffer, U_32 size) const; U_32 get_size() const { return taille; }; static U_I max_length() { return (U_I)(254)*254*254*254 - 1; }; private: U_32 taille; // max size of elastic buffer is 4GB which is large enough void randomize(unsigned char *a) const; U_I base_from_version(const archive_version & reading_ver) const; unsigned char get_low_mark(const archive_version & reading_ver) const; unsigned char get_high_mark(const archive_version & reading_ver) const; unsigned char get_low_mark() const { return 255; }; unsigned char get_high_mark() const { return 254; }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_lien.cpp0000644000175000017500000000673414636066467013637 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_lien.hpp" #include "tools.hpp" using namespace std; namespace libdar { cat_lien::cat_lien(const infinint & uid, const infinint & gid, U_16 perm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const string & name, const string & target, const infinint & fs_device) : cat_inode(uid, gid, perm, last_access, last_modif, last_change, name, fs_device) { points_to = target; set_saved_status(saved_status::saved); } cat_lien::cat_lien(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small) : cat_inode(dialog, pdesc, reading_ver, saved, small) { generic_file *ptr = nullptr; pdesc->check(small); if(small) ptr = pdesc->esc; else ptr = pdesc->stack; if(saved == saved_status::saved) tools_read_string(*ptr, points_to); } bool cat_lien::operator == (const cat_entree & ref) const { const cat_lien *ref_lien = dynamic_cast(&ref); if(ref_lien == nullptr) return false; else return points_to == ref_lien->points_to && cat_inode::operator == (ref); } const string & cat_lien::get_target() const { if(get_saved_status() != saved_status::saved) throw SRC_BUG; return points_to; } void cat_lien::set_target(string x) { set_saved_status(saved_status::saved); points_to = x; } void cat_lien::sub_compare(const cat_inode & other, bool isolated_mode) const { const cat_lien *l_other = dynamic_cast(&other); if(l_other == nullptr) throw SRC_BUG; // bad argument cat_inode::compare has a bug if(get_saved_status() == saved_status::saved && l_other->get_saved_status() == saved_status::saved) if(get_target() != l_other->get_target()) throw Erange("cat_lien:sub_compare", string(gettext("symbolic link does not point to the same target: ")) + get_target() + " <--> " + l_other->get_target()); } void cat_lien::inherited_dump(const pile_descriptor & pdesc, bool small) const { generic_file *ptr = nullptr; pdesc.check(small); if(small) ptr = pdesc.esc; else ptr = pdesc.stack; cat_inode::inherited_dump(pdesc, small); if(get_saved_status() == saved_status::saved) tools_write_string(*ptr, points_to); } } // end of namespace dar-2.7.15/src/libdar/cat_door.hpp0000644000175000017500000000545114636066467013653 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_door.hpp /// \brief class used in a catalogue to store solaris door filesystem entries /// \ingroup Private #ifndef CAT_DOOR_HPP #define CAT_DOOR_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the class for Door IPC (mainly for Solaris) class cat_door : public cat_file { public: cat_door(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & src, const path & che, const infinint & fs_device) : cat_file(xuid, xgid, xperm, last_access, last_modif, last_change, src, che, 0, fs_device, false) {}; cat_door(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, compression default_algo, bool small) : cat_file(dialog, pdesc, reading_ver, saved, default_algo, small) {}; cat_door(const cat_door & ref) = default; cat_door(cat_door && ref) = delete; cat_door & operator = (const cat_door & ref) = delete; cat_door & operator = (cat_door && ref) = delete; ~cat_door() = default; virtual bool operator == (const cat_entree & ref) const override; virtual unsigned char signature() const override { return 'o'; }; virtual std::string get_description() const override { return "door"; }; // inherited from class cat_file virtual generic_file *get_data(get_data_mode mode, std::shared_ptr delta_sig, U_I signature_block_size, std::shared_ptr delta_ref, const crc**checksum) const override; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/user_interaction_blind.cpp0000644000175000017500000000362414636066467016601 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_FCNTL_H #include #endif #if STDC_HEADERS #include #endif } // end extern "C" #include #include "user_interaction_blind.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "integers.hpp" #include "deci.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { // all methods are inlined } // end of namespace dar-2.7.15/src/libdar/slave_zapette.hpp0000644000175000017500000000575414636066467014735 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file slave_zapette.hpp /// \brief remote control for dar_slave. /// \ingroup Private /// /// Two objects communicates through a pair of pipes: /// - a zapette object is the dar side master class /// - a slave_zapette object is dar_slave side /// . /// they form a communication channel between dar/libdar (zapette side) /// and dar_slave/libdar (dar_slave side) #ifndef SLAVE_ZAPETTE_HPP #define SLAVE_ZAPETTE_HPP #include "../my_config.h" #include "generic_file.hpp" #include "contextual.hpp" namespace libdar { /// \addtogroup Private /// @{ /// this class answers to order given by a zapette object /// through a pair of pipes slave_zapette return information about /// a given local archive (single or multi slices). class slave_zapette { public: /// slave_zapette constructor /// \param[in] input is used to receive orders from an zapette object /// \param[in] output is used to return informations or data in answer to received orders /// \param[in] data is where the informations or data is taken from. Object must inherit from contextual slave_zapette(generic_file *input, generic_file *output, generic_file *data); slave_zapette(const slave_zapette & ref) = delete; slave_zapette(slave_zapette && ref) noexcept = delete; slave_zapette & operator = (const slave_zapette & ref) = delete; slave_zapette & operator = (slave_zapette && ref) noexcept = delete; ~slave_zapette(); /// main execution method for slave_zapette class /// this method implement a loop waiting for orders and answering to them /// the loop stops when a special order is received from the peer zapette object void action(); private: generic_file *in; ///< where to read orders from generic_file *out; ///< where to send requested info or data to generic_file *src; ///< where to read data from contextual *src_ctxt; ///< same as src but seen as contextual }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/slave_zapette.cpp0000644000175000017500000001561714636066467014727 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif } // end extern "C" #include #include #include "slave_zapette.hpp" #include "infinint.hpp" #include "tools.hpp" #include "trivial_sar.hpp" #include "sar.hpp" #include "zapette_protocol.hpp" using namespace std; namespace libdar { slave_zapette::slave_zapette(generic_file *input, generic_file *output, generic_file *data) { if(input == nullptr) throw SRC_BUG; if(output == nullptr) throw SRC_BUG; if(data == nullptr) throw SRC_BUG; if(input->get_mode() == gf_write_only) throw Erange("slave_zapette::slave_zapette", gettext("Input cannot be read")); if(output->get_mode() == gf_read_only) throw Erange("slave_zapette::slave_zapette", gettext("Cannot write to output")); if(data->get_mode() != gf_read_only) throw Erange("slave_zapette::slave_zapette", gettext("Data should be read-only")); in = input; out = output; src = data; src_ctxt = dynamic_cast(data); if(src_ctxt == nullptr) throw Erange("slave_zapette::slave_zapette", "Object given to data must inherit from contextual class"); } slave_zapette::~slave_zapette() { if(in != nullptr) delete in; if(out != nullptr) delete out; if(src != nullptr) delete src; } void slave_zapette::action() { request req; answer ans; char *buffer = nullptr; U_16 buf_size = 1024; buffer = new (nothrow) char[buf_size]; if(buffer == nullptr) throw Ememory("slave_zapette::action"); try { do { req.read(in); ans.serial_num = req.serial_num; if(req.size != REQUEST_SIZE_SPECIAL_ORDER) { ans.type = ANSWER_TYPE_DATA; if(src->skip(req.offset)) { // enlarge buffer if necessary if(req.size > buf_size) { if(buffer != nullptr) delete [] buffer; buffer = new (nothrow) char [req.size]; if(buffer == nullptr) throw Ememory("slave_zapette::action"); else buf_size = req.size; } ans.size = src->read(buffer, req.size); ans.write(out, buffer); } else // bad position { ans.size = 0; ans.write(out, nullptr); } } else // special orders { if(req.offset == REQUEST_OFFSET_END_TRANSMIT) // stop communication { ans.type = ANSWER_TYPE_DATA; ans.size = 0; ans.write(out, nullptr); } else if(req.offset == REQUEST_OFFSET_GET_FILESIZE) // return file size { ans.type = ANSWER_TYPE_INFININT; if(!src->skip_to_eof()) throw Erange("slave_zapette::action", gettext("Cannot skip at end of file")); ans.arg = src->get_position(); ans.write(out, nullptr); } else if(req.offset == REQUEST_OFFSET_CHANGE_CONTEXT_STATUS) // contextual status change requested { ans.type = ANSWER_TYPE_INFININT; ans.arg = 1; src_ctxt->set_info_status(req.info); ans.write(out, nullptr); } else if(req.offset == REQUEST_IS_OLD_START_END_ARCHIVE) // return whether the underlying archive has an old slice header or not { ans.type = ANSWER_TYPE_INFININT; ans.arg = src_ctxt->is_an_old_start_end_archive() ? 1 : 0; ans.write(out, nullptr); } else if(req.offset == REQUEST_GET_DATA_NAME) // return the data_name of the underlying sar { ans.type = ANSWER_TYPE_DATA; ans.arg = 0; ans.size = src_ctxt->get_data_name().size(); ans.write(out, (char *)(src_ctxt->get_data_name().data())); } else if(req.offset == REQUEST_FIRST_SLICE_HEADER_SIZE) { trivial_sar *src_triv = dynamic_cast(src); sar *src_sar = dynamic_cast(src); ans.type = ANSWER_TYPE_INFININT; if(src_triv != nullptr) ans.arg = src_triv->get_slice_header_size(); else if(src_sar != nullptr) ans.arg = src_sar->get_first_slice_header_size(); else ans.arg = 0; // means unknown ans.write(out, nullptr); } else if(req.offset == REQUEST_OTHER_SLICE_HEADER_SIZE) { trivial_sar *src_triv = dynamic_cast(src); sar *src_sar = dynamic_cast(src); ans.type = ANSWER_TYPE_INFININT; if(src_triv != nullptr) ans.arg = src_triv->get_slice_header_size(); else if(src_sar != nullptr) ans.arg = src_sar->get_non_first_slice_header_size(); else ans.arg = 0; // means unknown ans.write(out, nullptr); } else throw Erange("zapette::action", gettext("Received unknown special order")); } } while(req.size != REQUEST_SIZE_SPECIAL_ORDER || req.offset != REQUEST_OFFSET_END_TRANSMIT); } catch(...) { if(buffer != nullptr) delete [] buffer; throw; } if(buffer != nullptr) delete [] buffer; } } // end of namespace dar-2.7.15/src/libdar/candidates.hpp0000644000175000017500000000356114636066467014160 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file candidates.hpp /// \brief helper class for class data_tree to find the list of archive to restore for a given file /// \ingroup Private #ifndef CANDIDATES_HPP #define CANDIDATES_HPP #include "../my_config.h" #include "database_aux.hpp" #include "archive_num.hpp" #include #include namespace libdar { /// \addtogroup Private /// @{ ///helper class for data_tree class candidates { public: candidates(bool even_when_removed): ewr(even_when_removed) { clear(); }; void clear() { num.clear(); status.clear(); }; void add(archive_num val, db_etat st); db_lookup get_status() const; void set_the_set(std::set & archive) const; private: bool ewr; ///< even when removed, ignore removed status std::deque num; std::deque status; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/i_libdar_xform.hpp0000644000175000017500000001506114636066467015037 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file i_libdar_xform.hpp /// \brief API for dar_xform functionnality /// \ingroup Private #ifndef I_LIBDAR_XFORM_HPP #define I_LIBDAR_XFORM_HPP #include "../my_config.h" #include "entrepot_local.hpp" #include "infinint.hpp" #include "mem_ui.hpp" #include "label.hpp" #include "libdar_xform.hpp" namespace libdar { /// \addtogroup Private /// @{ /// class implementing the dar_xform feature class libdar_xform::i_libdar_xform : public mem_ui { public: /// the archive to transform is read from a set of slices /// \param[in] ui for user interaction, may be set to std::nullptr /// \param[in] chem the path where resides the archive /// \param[in] basename the basename of the archive /// \param[in] extension should be set to "dar" as always /// \param[in] min_digits the way slice number is written in files, /// use 0 if this feature was not used at creation time /// \param[in] execute command to execute before each new slice /// same substitution is available as archive_options_create::set_execute() i_libdar_xform(const std::shared_ptr & ui, const std::string & chem, const std::string & basename, const std::string & extension, const infinint & min_digits, const std::string & execute); /// the archive to transform is read from a named pipe /// \param[in] dialog for user interaction, may be set to std::nullptr /// \param[in] pipename named pipe where to read the archive from (single sliced one) i_libdar_xform(const std::shared_ptr & dialog, const std::string & pipename); /// < if pipename is set to "-" reading from standard input /// the archive to transform is read from a file descriptor open in read mode /// \param[in] dialog for user interaction, may be set to std::nullptr /// \param[in] filedescriptor the filedescriptor to reading the archive from i_libdar_xform(const std::shared_ptr & dialog, int filedescriptor); /// copy constructor is not allowed i_libdar_xform(const i_libdar_xform & ref) = delete; /// move constructor i_libdar_xform(i_libdar_xform && ref) noexcept = default; /// assignment operator is not allowed i_libdar_xform & operator = (const i_libdar_xform & ref) = delete; /// move assignment operator i_libdar_xform & operator = (i_libdar_xform && ref) noexcept = default; /// destructor ~i_libdar_xform() = default; /// the resulting archive is a written to disk possibly multi-sliced /// \param[in] path directory where to write the new archive to /// \param[in] basename archive base name to create /// \param[in] extension should be set to "dar" as always /// \param[in] allow_over whether to allow slice overwriting /// \param[in] warn_over whether to warn before overwriting a slice /// \param[in] pause the number of slice to pause asking the user for continuation. Set /// to zero to disable pausing /// \param[in] first_slice_size size of the first slice if different of the other. Set /// to zero to have the first slice having the same size as others /// \param[in] slice_size size of slices (except the first slice which may be different). /// set to zero if slicing is not wanted /// \param[in] slice_perm number written in octal corresponding to the permission of slices /// to create. if set to an empty string the slice permission will not be overriden and will /// follow the umask() value /// \param[in] slice_user user name or UID to assign slice to. Assuming the process has /// the permission/capabilities to change UID of files /// \param[in] slice_group group name or GID to assign slice to. Assuming the process has /// the permission/capabilities to change GID of files /// \param[in] hash hashing algorithm to rely on generate hash file for each slice. Use /// libdar::hash_algo::hash_none to disable this feature /// \param[in] min_digits numbering of slices in filename should be padded by as much zero /// to have no less than this number of digit. Use zero to disable this feature /// \param[in] execute command to execute after each slice has been completed. Same /// substitution is available as archive_options_create::set_execute void xform_to(const std::string & path, const std::string & basename, const std::string & extension, bool allow_over, bool warn_over, const infinint & pause, const infinint & first_slice_size, const infinint & slice_size, const std::string & slice_perm, const std::string & slice_user, const std::string & slice_group, hash_algo hash, const infinint & min_digits, const std::string & execute); /// the resulting archive is a single sliced archive sent to a filedescriptor /// \param[in] filedescriptor file descriptor open in write mode to write the archive to /// \param[in] execute command to execute after the archive has been completed. /// same string substitution available as described in other xform_to method void xform_to(int filedescriptor, const std::string & execute); private: bool can_xform; std::unique_ptr source; std::unique_ptr src_path; ///< may be null when reading from a pipe std::shared_ptr entrep_src; std::shared_ptr entrep_dst; bool format_07_compatible; label dataname; void init_entrep(); void xform_to(generic_file *dst); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/data_tree.hpp0000644000175000017500000002472014636066467014011 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file data_tree.hpp /// \brief base classes used to store entree information in dar_manager databases /// \ingroup Private #ifndef DATA_TREE_HPP #define DATA_TREE_HPP #include "../my_config.h" #include #include #include #include #include "infinint.hpp" #include "generic_file.hpp" #include "user_interaction.hpp" #include "path.hpp" #include "database_listing_callback.hpp" #include "database_aux.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the data_tree class stores presence of a given file in a set of archives /// the data associated to a given file are the different modification dates /// that this file has been found in the archive the database has been feed by class data_tree { public: data_tree(const std::string &name); data_tree(generic_file & f, unsigned char db_version); //< constructor does not read signature data_tree(const data_tree & ref) = default; data_tree(data_tree && ref) noexcept = default; data_tree & operator = (const data_tree & ref) = default; data_tree & operator = (data_tree && ref) noexcept = default; virtual ~data_tree() = default; virtual void dump(generic_file & f) const; //< dump signature followed by data constructor will read std::string get_name() const { return filename; }; void set_name(const std::string & name) { filename = name; }; /// returns the archives to restore in order to obtain the data that was defined just before (or at) the given date /// /// \param[out] archive is the set of archive to restore in sequence to obtain the requested data /// \param[in] date date above which to ignore data found in the database /// \param[in] even_when_removed is true when user requested to restore the file in its latest state even if it has been removed afterward /// \return the success of failure status of the requested lookup db_lookup get_data(std::set & archive, const datetime & date, bool even_when_removed) const; /// if EA has been saved alone later, returns in which version for the state of the file at the given date. db_lookup get_EA(archive_num & archive, const datetime & date, bool even_when_removed) const; /// return the date of file's last modification date within the give archive and whether the file has been saved or deleted bool read_data(archive_num num, datetime & val, db_etat & present) const; /// return the date of last inode change and whether the EA has been saved or deleted bool read_EA(archive_num num, datetime & val, db_etat & present) const; void set_data(const archive_num & archive, const datetime & date, db_etat present) { set_data(archive, date, present, nullptr, nullptr); }; void set_data(const archive_num & archive, const datetime & date, db_etat present, const crc *base, const crc *result) { last_mod[archive] = status_plus(date, present, base, result); (void) check_delta_validity(); }; void set_EA(const archive_num & archive, const datetime & date, db_etat present) { status sta(date, present); last_change[archive] = sta; }; /// check date order between archives withing the database ; throw Erange if problem found with date order virtual bool check_order(user_interaction & dialog, const path & current_path, bool & initial_warn) const { return check_map_order(dialog, last_mod, current_path, "data", initial_warn) && check_map_order(dialog, last_change, current_path, "EA", initial_warn); }; /// add deleted entry if no object of the current archive exist and the entry of the previous archive is already present. /// \param[in] archive is the number of the archive to finalize /// \param[in] deleted_date date of deletion to use for inode removal when no /// information can be grabbed from the archive (this date is taken from the /// parent dir last modification date) /// \param[in] ignore_archive_greater_or_equal ignore archives which number /// is greater or equal than "ignore_archive_greater_or_equal" as if they were not /// present in the database. If set to zero, no archive is ignored. virtual void finalize(const archive_num & archive, const datetime & deleted_date, const archive_num & ignore_archive_greater_or_equal); /// return true if the corresponding file is no more located in any archive (thus, the object is no more usefull in the base) virtual bool remove_all_from(const archive_num & archive_to_remove, const archive_num & last_archive); /// list where is saved this file void listing(database_listing_get_version_callback callback, void *tag) const; virtual void apply_permutation(archive_num src, archive_num dst); /// decrement archive numbers above num virtual void skip_out(archive_num num); virtual void compute_most_recent_stats(std::deque & data, std::deque & ea, std::deque & total_data, std::deque & total_ea) const; virtual char obj_signature() const { return signature(); }; static char signature() { return 't'; }; // fix corruption case that was brought by bug in release 2.4.0 to 2.4.9 virtual bool fix_corruption(); // return true whether corruption could be fixed (meaning this entry can be safely removed from base) private: static constexpr const char * const ETAT_SAVED = "S"; static constexpr const char * const ETAT_PATCH = "O"; static constexpr const char * const ETAT_PATCH_UNUSABLE = "U"; static constexpr const char * const ETAT_PRESENT = "P"; static constexpr const char * const ETAT_REMOVED = "R"; static constexpr const char * const ETAT_ABSENT = "A"; static constexpr const char * const ETAT_INODE = "I"; static constexpr unsigned char STATUS_PLUS_FLAG_ME = 0x01; static constexpr unsigned char STATUS_PLUS_FLAG_REF = 0x02; class status { public: status(): date(0) { present = db_etat::et_absent; }; status(const datetime & d, db_etat p) { date = d; present = p; }; status(const status & ref) = default; status(status && ref) noexcept = default; status & operator = (const status & ref) = default; status & operator = (status && ref) noexcept = default; virtual ~status() = default; datetime date; //< date of the event db_etat present; //< file's status in the archive virtual void dump(generic_file & f) const; //< write the struct to file virtual void read(generic_file &f, //< set the struct from file unsigned char db_version); }; class status_plus : public status { public: status_plus() { base = result = nullptr; }; status_plus(const datetime & d, db_etat p, const crc *xbase, const crc *xresult); status_plus(const status_plus & ref): status(ref) { copy_from(ref); }; status_plus(status_plus && ref) noexcept: status(std::move(ref)) { nullifyptr(); move_from(std::move(ref)); }; status_plus & operator = (const status_plus & ref) { detruit(); copy_from(ref); return *this; }; status_plus & operator = (status_plus && ref) noexcept { status::operator = (std::move(ref)); move_from(std::move(ref)); return *this; }; ~status_plus() { detruit(); }; crc *base; //< only present for s_delta status, to have a link with the file to apply the patch to crc *result; //< present for s_delta, s_saved, s_not_saved this is the crc of the data (or crc of the data resulting from the patch) void dump(generic_file & f) const; //< write the struct to file void read(generic_file &f, //< set the struct from file unsigned char db_version); private: void nullifyptr() noexcept { base = result = nullptr; }; void copy_from(const status_plus & ref); void move_from(status_plus && ref) noexcept; void detruit(); }; std::string filename; std::map last_mod; //< key is archive number ; value is last_mod time std::map last_change; //< key is archive number ; value is last_change time // when false is returned, this means that the user wants to ignore subsequent error of the same type // else either no error yet met or user want to continue receiving the same type of error for other files // in that later case initial_warn is set to false (first warning has been shown). template bool check_map_order(user_interaction & dialog, const std::map the_map, const path & current_path, const std::string & field_nature, bool & initial_warn) const; bool check_delta_validity(); // return true if no error has been met about delta patch (no delta is broken, missing its reference) /// gives new archive number when an database has its archive reordered /// \param[in] src the archive number to move /// \param[in] dst the new position of the archive number given by src /// \param[in] x any archive number in the database, which new position is to be calculated in regard to the src -> dst move /// \return the new archive number of archive x in regard to the src -> dst move static archive_num data_tree_permutation(archive_num src, archive_num dst, archive_num x); /// helper method to provide information to a database_listing_get_version_callback static void display_line(database_listing_get_version_callback callback, void *tag, archive_num num, const datetime *data, db_etat data_presence, const datetime *ea, db_etat ea_presence); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_directory.hpp0000644000175000017500000001625114636067146014707 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_directory.hpp /// \brief class used to organize objects in tree in catalogue as like directories in a filesystem /// \ingroup Private #ifndef CAT_DIRECTORY_HPP #define CAT_DIRECTORY_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_inode.hpp" #ifdef LIBDAR_FAST_DIR #include #endif #include namespace libdar { class cat_eod; /// \addtogroup Private /// @{ /// the cat_directory inode class class cat_directory : public cat_inode { public : cat_directory(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & xname, const infinint & device); cat_directory(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, entree_stats & stats, std::map & corres, compression default_algo, bool lax, bool only_detruit, // objects of other class than detruit and cat_directory are not built in memory bool small); cat_directory(const cat_directory &ref); // only the inode part is build, no children is duplicated (empty dir) cat_directory(cat_directory && ref) noexcept; cat_directory & operator = (const cat_directory & ref); // set the inode part *only* no subdirectories/subfiles are copies or removed. cat_directory & operator = (cat_directory && ref); ~cat_directory() noexcept(false); // detruit aussi tous les fils et se supprime de son 'parent' /// attention this compares only the directories themselves, not the list of their children virtual bool operator == (const cat_entree & ref) const override; void add_children(cat_nomme *r); // when r is a cat_directory, 'parent' is set to 'this' bool has_children() const { return !ordered_fils.empty(); }; void reset_read_children() const; void end_read() const; bool read_children(const cat_nomme * &r) const; // read the direct children of the cat_directory, returns false if no more is available // remove all entry not yet read by read_children void tail_to_read_children(); /// remove the given entry from the catalogue /// \note read_children() is taken into account by this operation, /// no need to call reset_read_children(), if the argument removed was the /// one about to be read by read_children() the one following the removed entry /// will be returned the next time read_children() is invoked. void remove(const std::string & name); cat_directory * get_parent() const { return parent; }; bool search_children(const std::string &name, const cat_nomme *&ref) const; // using is_more_recent_than() from cat_inode class // using method has_changed_since() from cat_inode class virtual unsigned char signature() const override { return 'd'; }; virtual std::string get_description() const override { return "folder"; }; /// detemine whether some data has changed since archive of reference in this cat_directory or subdirectories bool get_recursive_has_changed() const { return recursive_has_changed; }; /// ask recursive update for the recursive_has_changed field void recursive_has_changed_update() const; /// get the number of "cat_nomme" entry directly containted in this cat_directory (no recursive call) infinint get_dir_size() const { return ordered_fils.size(); }; /// get then number of "cat_nomme" entry contained in this cat_directory and subdirectories (recursive call) infinint get_tree_size() const; /// get the number of entry having some EA set in the cat_directory tree (recursive call) infinint get_tree_ea_num() const; /// get the number of entry that are hard linked inode (aka mirage in dar implementation) (recursive call) infinint get_tree_mirage_num() const; // for each mirage found (hard link implementation) in the cat_directory tree, add its etiquette to the returned // list with the number of reference that has been found in the tree. (map[etiquette] = number of occurence) // from outside of class cat_directory, the given argument is expected to be an empty map. void get_etiquettes_found_in_tree(std::map & already_found) const; /// whether this cat_directory is empty or not bool is_empty() const { return ordered_fils.empty(); }; /// recursively remove all mirage entries void remove_all_mirages_and_reduce_dirs(); /// recursively set all mirage inode_wrote flag void set_all_mirage_s_inode_wrote_field_to(bool val) const; /// set the value of inode_dumped for all mirage (recusively) void set_all_mirage_s_inode_dumped_field_to(bool val) const; virtual cat_entree *clone() const override { return new (std::nothrow) cat_directory(*this); }; const infinint & get_size() const { recursive_update_sizes(); return x_size; }; const infinint & get_storage_size() const { recursive_update_sizes(); return x_storage_size; }; void recursively_set_to_unsaved_data_and_FSA(); /// overwrite virtual method of cat_entree to propagate the action to all entries of the directory tree virtual void change_location(const smart_pointer & pdesc) override; protected: virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; private: static const cat_eod fin; mutable infinint x_size; mutable infinint x_storage_size; mutable bool updated_sizes; cat_directory *parent; #ifdef LIBDAR_FAST_DIR std::map fils; // used for fast lookup #endif std::deque ordered_fils; mutable std::deque::const_iterator it; ///< next entry to be returned by read_children mutable bool recursive_has_changed; void init() noexcept; void clear(); void recursive_update_sizes() const; void recursive_flag_size_to_update() const; void erase_ordered_fils(std::deque::const_iterator debut, std::deque::const_iterator fin); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_prise.hpp0000644000175000017500000000552114636066467014030 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_prise.hpp /// \brief class to record filesystem (UNIX) sockets in a catalogue /// \ingroup Private #ifndef CAT_PRISE_HPP #define CAT_PRISE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_inode.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the Unix socket inode class class cat_prise : public cat_inode { public : cat_prise(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & xname, const infinint & fs_device): cat_inode(xuid, xgid, xperm, last_access, last_modif, last_change, xname, fs_device) { set_saved_status(saved_status::saved); }; cat_prise(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small): cat_inode(dialog, pdesc, reading_ver, saved, small) {}; cat_prise(const cat_prise & ref) = default; cat_prise(cat_prise && ref) noexcept = default; cat_prise & operator = (const cat_prise & ref) = default; cat_prise & operator = (cat_prise && ref) = default; ~cat_prise() = default; virtual bool operator == (const cat_entree & ref) const override; // using dump from cat_inode class // using method is_more_recent_than() from cat_inode class // using method has_changed_since() from cat_inode class /// inherited from cat_entree virtual unsigned char signature() const override { return 's'; }; virtual std::string get_description() const override { return "socket"; }; /// inherited from cat_entree virtual cat_entree *clone() const override { return new (std::nothrow) cat_prise(*this); }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/user_interaction5.hpp0000644000175000017500000004452214636067146015520 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file user_interaction5.hpp /// \brief API v5 backward compatible class user_interaction /// \ingroup API5 #ifndef USER_INTERACTION5_HPP #define USER_INTERACTION5_HPP #include "../my_config.h" #include #include #include "user_interaction.hpp" #include "secu_string.hpp" namespace libdar5 { /// \addtogroup API5 /// @{ using libdar::secu_string; using libdar::U_I; using libdar::infinint; using libdar::Euser_abort; using libdar::Elibcall; using libdar::Ebug; using libdar::Egeneric; using libdar::Ememory; /// This is a pure virtual class that is used by libdar when interaction with the user is required. /// You can base your own class on it using C++ inheritance /// or use the class user_interaction_callback which implements /// the interaction based on callback functions. /// The user_interaction class is used by libdar in the following circumpstances: /// - when is required a boolean answer to a question the pause() method is used /// - when a warning needs to be displayed to the user the warning() method is used /// - when a directory listing needs to be returned to the user the listing() method is used /// . /// the printf() method is built over the warning() methods to display a formated message /// it has not to be redefined in any inherited class. /// If you want to define you own class as inherited class of user_interaction /// you need to overwrite: /// - the clone() method. It is used to make local temporary copies of objets /// in libdar. It acts like the constructor copy but is virtual. /// - the pause() method /// - the warning() method /// - the listing() method (this is not mandatory). inherited classes *can* /// overwrite the listing() method, which will be used if the use_listing /// is set to true thanks to the set_use_listing() protected method. /// In that case the listing of archive contents is done thanks to this listing() /// method instead of the warning() method. /// - get_string() method /// - get_secu_string() method /// . /// WARNING ! /// if your own class has specific fields, you will probably /// need to redefine the copy constructor as well as operator = /// if you don't understand this and why, don't play trying making your /// own class, and/or read good C++ book about canonical form /// of a C++ class, as well as how to properly make an inherited class. /// And don't, complain if libdar segfault or core dumps. Libdar /// *needs* to make local copies of these objects, if the copy constructor /// is not properly defined in your inherited class this will crash the application. class user_interaction : public libdar::user_interaction { public: /// class constructor. user_interaction(); user_interaction(const user_interaction & ref) = default; user_interaction(user_interaction && ref) noexcept = default; user_interaction & operator = (const user_interaction & ref) = default; user_interaction & operator = (user_interaction && ref) noexcept = default; virtual ~user_interaction() = default; /// method added for backward compatibility with API v5 /// \note warning() has been renamed message() in libdar::user_interaction /// to let libdar5::user_interaction intercept the inherted_message() to /// inherited_warning() and keep implementing the warning_with_more() feature /// as warning() does not exist in the parent class and some API v5 program /// may call it we add a warning() methode here: void warning(const std::string & msg) { message(msg); }; /// method used to ask a boolean question to the user. /// \param[in] message is the message to be displayed, that is the question. /// \exception Euser_abort If the user answer "false" or "no" to the question the method /// must throw an exception of type "Euser_abort". virtual void pause(const std::string & message) { if(!pause2(message)) throw Euser_abort(message); }; /// alternative method to the pause() method /// \param[in] message The boolean question to ask to the user /// \return the answer of the user (true/yes or no/false) /// \note either pause2() or pause() *must* be overwritten, but not both. /// libdar always calls pause() which default implementation relies on pause2() where it converts negative /// return from pause2() by throwing the appropriated exception. As soon as you overwrite pause(), /// pause2() is no more used. virtual bool pause2(const std::string & message) { throw Elibcall("user_interaction::pause2", "user_interaction::pause() or pause2() must be overwritten !"); }; /// method used to ask a question that needs an arbitrary answer. /// \param[in] message is the question to display to the user. /// \param[in] echo is set to false is the answer must not be shown while the user answers. /// \return the user's answer. virtual std::string get_string(const std::string & message, bool echo) = 0; /// same a get_string() but uses secu_string instead /// \param[in] message is the question to display to the user. /// \param[in] echo is set to false is the answer must not be shown while the user answers. /// \return the user's answer. virtual secu_string get_secu_string(const std::string & message, bool echo) = 0; /// optional method to use if you want file listing splitted in several fields. /// If you need to use this feature, you have then to supply an implementation for this method, /// in your inherited class which will be called by libdar in place of the warning method /// You then also have to call the set_use_listing() method with true as parameter /// from the constructor of your inherited class (for example) to tell libdar that the listing() method is /// to be used in place of the warning() method for archive listing. /// \param[in] flag is the given information about the EA, compression, presence of saved data. /// \param[in] perm is the access permission of the file. /// \param[in] uid User ID of the file. /// \param[in] gid Group ID of the file. /// \param[in] size file size. /// \param[in] date file modification date. /// \param[in] filename file name. /// \param[in] is_dir true if file is a directory. /// \param[in] has_children true if file is a directory which is not empty. /// \note This is not a pure virtual method, this is normal, /// so your inherited class is not obliged to overwrite it. virtual void listing(const std::string & flag, const std::string & perm, const std::string & uid, const std::string & gid, const std::string & size, const std::string & date, const std::string & filename, bool is_dir, bool has_children); /// optional method to use if you want dar_manager database contents listing split in several fields. /// if you want to use this feature, you have then to supply an implementation for this method /// in your inherited class, method that will be called by libdar in place of the warning method. /// You will also have to call the set_use_dar_manager_show_files() protected method with true as argument /// from the constructor of your inherited class to tell libdar to use the dar_manager_show_files() /// method in place of the warning() method. /// \param[in] filename name of the file /// \param[in] data_change whether the backup owns the most recent data for the file /// \param[in] ea_change whether the backup owns the most recent Extended Attributes for the file /// \note this method can be set for database::show_files() method to call it virtual void dar_manager_show_files(const std::string & filename, bool data_change, bool ea_change); /// optional method to use if you want dar_manager database archive listing split in several fields /// if you want to use this feature, you have then to supply an implementation for this method /// in your inherited class, method that will be called by libdar in place of the warning method. /// You will also have to call the set_use_dar_manager_contents() protected method with true as argument /// from the constructor of your inherited class to tell libdar to use the dar_manager_contents() /// method in place of the warning() method. /// \param[in] number is the number of the archive in the database /// \param[in] chemin recorded path where to find this archive /// \param[in] archive_name basename of this archive /// \note this method can be set for database::show_contents() to call it virtual void dar_manager_contents(U_I number, const std::string & chemin, const std::string & archive_name); /// optional method to use if you want dar_manager statistics listing split in several fields /// if you want to use this feature, you have then to supply an implementation for this method /// in your inherited class, method that will be called by libdar in place of the warning method. /// You will also have to call the set_use_dar_manager_statistics() protected method with true as argument /// from the constructor of your inherited class to tell libdar to use the dar_manager_statistics() /// method in place of the warning() method. /// \param[in] number archive number /// \param[in] data_count amount of file which last version is located in this archive /// \param[in] total_data total number of file covered in this database /// \param[in] ea_count amount of EA which last version is located in this archive /// \param[in] total_ea total number of file that have EA covered by this database /// \note this method can be set for database::show_most_recent_stats() method to call it virtual void dar_manager_statistics(U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea); /// optional method to use if you want dar_manager statistics listing split in several fields /// if you want to use this feature, you have then to supply an implementation for this method /// in your inherited class, method that will be called by libdar in place of the warning method. /// You will also have to call the set_use_dar_manager_show_version() protected method with true as argument /// from the constructor of your inherited class to tell libdar to use the dar_manager_show_version() /// method in place of the warning() method. /// \param[in] number archive number /// \param[in] data_date is the last modification date of the requested file in thie archive whose number is "number" /// \param[in] data_presence is the nature of this modification, true if the data was saved, false if it was deleted /// \param[in] ea_date is the date of the EA for the requested file in the archive whose number is "number" /// \param[in] ea_presence is the nature of this modification, true if the EAs were saved, false if they were deleted /// \note this method can be set for database::show_version() method to call it virtual void dar_manager_show_version(U_I number, const std::string & data_date, const std::string & data_presence, const std::string & ea_date, const std::string & ea_presence); /// for libdar to know if it is interesting to use listing(), dar_manager_show_files(), /// dar_manager_contents(), dar_manager_statistics() or to keep reporting listing thanks /// to the warning() method, /// this is not a virtual method, it has not to be overwritten in inherited classes. bool get_use_listing() const { return use_listing; }; /// this is not a virtual method, it has not to be overwritten in inherited classes. bool get_use_dar_manager_show_files() const { return use_dar_manager_show_files; }; /// this is not a virtual method, it has not to be overwritten in inherited classes. bool get_use_dar_manager_contents() const { return use_dar_manager_contents; }; /// this is not a virtual method, it has not to be overwritten in inherited classes. bool get_use_dar_manager_statistics() const { return use_dar_manager_statistics; }; /// this is not a virtual method, it has not to be overwritten in inherited classes. bool get_use_dar_manager_show_version() const { return use_dar_manager_show_version; }; virtual void printf(const char *format, ...) override; /// make a newly allocated object which has the same properties as "this". /// This *is* a virtual method, it *must* be overwritten in any inherited class /// copy constructor and = operator may have to be overwritten too if necessary /// Warning ! /// clone() must throw exception if necessary (Ememory), but never /// return a nullptr pointer ! virtual user_interaction *clone() const = 0; /// make a pause each N line of output when calling the warning method /// \param[in] num is the number of line to display at once, zero for unlimited display /// \note. Since API 3.1, the warning method is no more a pure virtual function /// you need to call the parent warning method in your method for this warning_with_more /// method works as expected. void warning_with_more(U_I num) { at_once = num; count = 0; }; protected: /// method to be called with true as argument if you have defined a listing() method. /// in the constructor of any inherited class that define a listing() method /// it is advisable to call set_use_listing() with true as argument for libdar /// knows that the listing() call has to be used in place of the warning() call /// for file listing. void set_use_listing(bool val) { use_listing = val; }; /// method to be called with true as argument if you have defined a dar_manager_show_files() method. void set_use_dar_manager_show_files(bool val) { use_dar_manager_show_files = val; }; /// method to be called with true as argument if you have defined a dar_manager_contents() method. void set_use_dar_manager_contents(bool val) { use_dar_manager_contents = val; }; /// method to be called with true as argument if you have defined a dar_manager_statistics() method. void set_use_dar_manager_statistics(bool val) { use_dar_manager_statistics = val; }; /// method to be called with true as argument if you have defined a dar_manager_show_version() method. void set_use_dar_manager_show_version(bool val) { use_dar_manager_show_version = val; }; /// need to be overwritten in place of the warning() method since API 3.1.x // inherited from libdar::user_interaction virtual void inherited_message(const std::string & message) override; virtual bool inherited_pause(const std::string & message) override; virtual std::string inherited_get_string(const std::string & message, bool echo) override; virtual secu_string inherited_get_secu_string(const std::string & message, bool echo) override; /// to be defined by inherited classes virtual void inherited_warning(const std::string & message) = 0; private: bool use_listing; bool use_dar_manager_show_files; bool use_dar_manager_contents; bool use_dar_manager_statistics; bool use_dar_manager_show_version; U_I at_once, count; }; /// convert a user_interaction to a shared_pointer on a clone of that user_interaction extern std::shared_ptr user_interaction5_clone_to_shared_ptr(user_interaction & dialog); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/erreurs.hpp0000644000175000017500000003712214636067146013543 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file erreurs.hpp /// \brief contains all the excetion class thrown by libdar /// \ingroup API #ifndef ERREURS_HPP #define ERREURS_HPP #include "../my_config.h" #include #include #include "integers.hpp" namespace libdar { /// \addtogroup API /// @{ /// a routine to change NLS domaine forth and back for inline routines extern const char *dar_gettext(const char *); /// this is the parent class of all exception classes. /// this is a pure virtual class that provide some simple /// mechanisme to carry the information about the cause of the exception, /// as well as some a complex mechanim which not used often in libdar /// that keep trace, for each exception throwing process, of the different /// calls by which the current exception has been exiting. class Egeneric { public : /// the constructor Egeneric(const std::string &source, const std::string &message); /// copy constructor Egeneric(const Egeneric & ref) = default; /// move constructor Egeneric(Egeneric && ref) = default; /// assignment operator Egeneric & operator = (const Egeneric & ref) = default; /// move operator Egeneric & operator = (Egeneric && ref) noexcept = default; /// the destructor virtual ~Egeneric() = default; /// add more detailed couple of information to the exception void stack(const std::string & passage, const std::string & message = "") { pile.push_back(niveau(passage, message)); }; void stack(const std::string && passage, const std::string && message = "") { pile.push_back(niveau(std::move(passage), std::move(message))); }; /// get the message explaing the nature of the exception /// This is probably the only method you will use for all the /// the exception, as you will not have to create such objects /// and will only need to get the error message thanks to this /// method const std::string & get_message() const { return pile.front().objet; }; /// get the call function which has thrown this exception const std::string & get_source() const { return pile.front().lieu; }; /// retrieve the objet (object) associated to a given "lieu" (location) from the stack /// \param[in] location key to look for the value of /// \return returns an empty string if key is not found in the stack const std::string & find_object(const std::string & location) const; /// prepend error message by the given string void prepend_message(const std::string & context); /// return a string result of the exception information dump std::string dump_str() const; protected : virtual std::string exceptionID() const = 0; private : struct niveau { niveau(const std::string & ou, const std::string & quoi) { lieu = ou; objet = quoi; }; niveau(std::string && ou, std::string && quoi) { lieu = std::move(ou); objet = std::move(quoi); }; niveau(const niveau & ref) = default; niveau(niveau && ref) noexcept = default; niveau & operator = (const niveau & ref) = default; niveau & operator = (niveau && ref) noexcept = default; ~niveau() = default; std::string lieu, objet; }; std::deque pile; static const std::string empty_string; }; /// exception used when memory has been exhausted /// the inherited get_message() method is probably /// the only one you will need to use class Ememory : public Egeneric { public: Ememory(const std::string &source) : Egeneric(source, dar_gettext("Lack of Memory")) {}; Ememory(const Ememory & ref) = default; Ememory(Ememory && ref) = default; Ememory & operator = (const Ememory & ref) = default; Ememory & operator = (Ememory && ref) = default; ~Ememory() = default; protected: Ememory(const std::string &source, const std::string & message) : Egeneric(source, message) {}; virtual std::string exceptionID() const override { return "MEMORY"; }; }; /// exception used when secure memory has been exhausted class Esecu_memory : public Ememory { public: Esecu_memory(const std::string &source) : Ememory(source, dar_gettext("Lack of Secured Memory")) {}; Esecu_memory(const Esecu_memory & ref) = default; Esecu_memory(Esecu_memory && ref) = default; Esecu_memory & operator = (const Esecu_memory & ref) = default; Esecu_memory & operator = (Esecu_memory && ref) = default; ~Esecu_memory() = default; protected: virtual std::string exceptionID() const override { return "SECU_MEMORY"; }; }; #define SRC_BUG Ebug(__FILE__, __LINE__) // #define XMT_BUG(exception, call) exception.stack(call, __FILE__, __LINE__) /// exception used to signal a bug. A bug is triggered when reaching some code that should never be reached class Ebug : public Egeneric { public : Ebug(const std::string & file, S_I line); Ebug(const Ebug & ref) = default; Ebug(Ebug && ref) = default; Ebug & operator = (const Ebug & ref) = default; Ebug & operator = (Ebug && ref) = default; ~Ebug() = default; using Egeneric::stack; // to avoid warning with clang void stack(const std::string & passage, const std::string & file, const std::string & line); protected : virtual std::string exceptionID() const override { return "BUG"; }; }; /// exception used when arithmetic error is detected when operating on infinint /// the inherited get_message() method is probably /// the only one you will need to use class Einfinint : public Egeneric { public : Einfinint(const std::string & source, const std::string & message) : Egeneric(source, message) {}; Einfinint(const Einfinint & ref) = default; Einfinint(Einfinint && ref) = default; Einfinint & operator = (const Einfinint & ref) = default; Einfinint & operator = (Einfinint && ref) = default; ~Einfinint() = default; protected : virtual std::string exceptionID() const override { return "INFININT"; }; }; /// exception used when a limitint overflow is detected, the maximum value of the limitint has been exceeded /// the inherited get_message() method is probably /// the only one you will need to use class Elimitint : public Egeneric { public : Elimitint() : Egeneric("", dar_gettext("Cannot handle such a too large integer. Use a full version of libdar (compiled to rely on the \"infinint\" integer type) to solve this problem")) {}; Elimitint(const Elimitint & ref) = default; Elimitint(Elimitint && ref) = default; Elimitint & operator = (const Elimitint & ref) = default; Elimitint & operator = (Elimitint && ref) = default; ~Elimitint() = default; protected : virtual std::string exceptionID() const override { return "LIMITINT"; }; }; /// exception used to signal range error /// the inherited get_message() method is probably /// the only one you will need to use class Erange : public Egeneric { public : Erange(const std::string & source, const std::string & message) : Egeneric(source, message) {}; Erange(const Erange & ref) = default; Erange(Erange && ref) = default; Erange & operator = (const Erange & ref) = default; Erange & operator = (Erange && ref) = default; ~Erange() = default; protected : virtual std::string exceptionID() const override { return "RANGE"; }; }; /// exception used to signal convertion problem between infinint and string (decimal representation) /// the inherited get_message() method is probably /// the only one you will need to use /// see also the class deci class Edeci : public Egeneric { public : Edeci(const std::string & source, const std::string & message) : Egeneric(source, message) {}; Edeci(const Edeci & ref) = default; Edeci(Edeci && ref) = default; Edeci & operator = (const Edeci & ref) = default; Edeci & operator = (Edeci && ref) = default; ~Edeci() = default; protected : virtual std::string exceptionID() const override { return "DECI"; }; }; /// exception used when a requested feature is not (yet) implemented /// the inherited get_message() method is probably /// the only one you will need to use class Efeature : public Egeneric { public : Efeature(const std::string & message) : Egeneric("", message) {}; Efeature(const Efeature & ref) = default; Efeature(Efeature && ref) = default; Efeature & operator = (const Efeature & ref) = default; Efeature & operator = (Efeature && ref) = default; ~Efeature() = default; protected : virtual std::string exceptionID() const override { return "UNIMPLEMENTED FEATURE"; }; }; /// exception used when hardware problem is found /// the inherited get_message() method is probably /// the only one you will need to use class Ehardware : public Egeneric { public : Ehardware(const std::string & source, const std::string & message) : Egeneric(source, message) {}; Ehardware(const Ehardware & ref) = default; Ehardware(Ehardware && ref) = default; Ehardware & operator = (const Ehardware & ref) = default; Ehardware & operator = (Ehardware && ref) = default; ~Ehardware() = default; protected : virtual std::string exceptionID() const override { return "HARDWARE ERROR"; }; }; /// exception used to signal that the user has aborted the operation /// the inherited get_message() method is probably /// the only one you will need to use class Euser_abort : public Egeneric { public : Euser_abort(const std::string & msg) : Egeneric("",msg) {}; Euser_abort(const Euser_abort & ref) = default; Euser_abort(Euser_abort && ref) = default; Euser_abort & operator = (const Euser_abort & ref) = default; Euser_abort & operator = (Euser_abort && ref) = default; ~Euser_abort() = default; protected : virtual std::string exceptionID() const override { return "USER ABORTED OPERATION"; }; }; /// exception used when an error concerning the treated data has been met /// the inherited get_message() method is probably /// the only one you will need to use class Edata : public Egeneric { public : Edata(const std::string & msg) : Egeneric("", msg) {}; Edata(const Edata & ref) = default; Edata(Edata && ref) = default; Edata & operator = (const Edata & ref) = default; Edata & operator = (Edata && ref) = default; ~Edata() = default; protected : virtual std::string exceptionID() const override { return "ERROR IN TREATED DATA"; }; }; /// exception used when error the inter-slice user command returned an error code /// the inherited get_message() method is probably /// the only one you will need to use class Escript : public Egeneric { public : Escript(const std::string & source, const std::string & msg) : Egeneric(source ,msg) {}; Escript(const Escript & ref) = default; Escript(Escript && ref) = default; Escript & operator = (const Escript & ref) = default; Escript & operator = (Escript && ref) = default; ~Escript() = default; protected : virtual std::string exceptionID() const override { return "USER ABORTED OPERATION"; }; }; /// exception used to signal an error in the argument given to libdar call of the API /// the inherited get_message() method is probably /// the only one you will need to use class Elibcall : public Egeneric { public : Elibcall(const std::string & source, const std::string & msg) : Egeneric(source ,msg) {}; Elibcall(const Elibcall & ref) = default; Elibcall(Elibcall && ref) = default; Elibcall & operator = (const Elibcall & ref) = default; Elibcall & operator = (Elibcall && ref) = default; ~Elibcall() = default; protected : virtual std::string exceptionID() const override { return "USER ABORTED OPERATION"; }; }; /// exception used when a requested fearture has not beed activated at compilation time /// the inherited get_message() method is probably /// the only one you will need to use class Ecompilation : public Egeneric { public : Ecompilation(const std::string & msg) : Egeneric("" ,msg) {}; Ecompilation(const Ecompilation & ref) = default; Ecompilation(Ecompilation && ref) = default; Ecompilation & operator = (const Ecompilation & ref) = default; Ecompilation & operator = (Ecompilation && ref) = default; ~Ecompilation() = default; protected : virtual std::string exceptionID() const override { return "FEATURE DISABLED AT COMPILATION TIME"; }; }; /// exception used when the thread libdar is running in is asked to stop class Ethread_cancel : public Egeneric { public: Ethread_cancel(bool now, U_64 x_flag) : Egeneric("", now ? dar_gettext("Thread cancellation requested, aborting as soon as possible") : dar_gettext("Thread cancellation requested, aborting as properly as possible")) { immediate = now; flag = x_flag; }; Ethread_cancel(const Ethread_cancel & ref) = default; Ethread_cancel(Ethread_cancel && ref) = default; Ethread_cancel & operator = (const Ethread_cancel & ref) = default; Ethread_cancel & operator = (Ethread_cancel && ref) = default; ~Ethread_cancel() = default; bool immediate_cancel() const { return immediate; }; U_64 get_flag() const { return flag; }; protected: virtual std::string exceptionID() const override { return "THREAD CANCELLATION REQUESTED, ABORTING"; }; private: bool immediate; U_64 flag; }; /// exception used to carry system error class Esystem : public Egeneric { public: enum io_error { io_exist, //< file already exists (write mode) io_absent, //< file does not exist (read mode) io_access, //< permission denied (any mode) io_ro_fs //< read-only filesystem (write mode/read-write mode) }; Esystem(const std::string & source, const std::string & message, io_error code); Esystem(const Esystem & ref) = default; Esystem(Esystem && ref) = default; Esystem & operator = (const Esystem & ref) = default; Esystem & operator = (Esystem && ref) = default; ~Esystem() = default; io_error get_code() const { return x_code; }; protected: virtual std::string exceptionID() const override { return "SYSTEM ERROR MET"; }; private: io_error x_code; }; /// exception used to report authentication error class Enet_auth : public Egeneric { public: Enet_auth(const std::string & message): Egeneric("on the network", message) {}; Enet_auth(const Enet_auth & ref) = default; Enet_auth(Enet_auth && ref) = default; Enet_auth & operator = (const Enet_auth & ref) = default; Enet_auth & operator = (Enet_auth && ref) = default; ~Enet_auth() = default; protected: virtual std::string exceptionID() const override { return "NETWORK AUTHENTICATION ERROR"; }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive_aux.cpp0000644000175000017500000000652014636067146014343 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_GCRYPT_H #ifndef GCRYPT_NO_DEPRECATED #define GCRYPT_NO_DEPRECATED #endif #include #endif } #include "archive_aux.hpp" #include "erreurs.hpp" #include "tools.hpp" using namespace std; namespace libdar { string hash_algo_to_string(hash_algo algo) { switch(algo) { case hash_algo::none: throw SRC_BUG; case hash_algo::md5: return "md5"; case hash_algo::sha1: return "sha1"; case hash_algo::sha512: return "sha512"; case hash_algo::argon2: return "argon2"; default: throw SRC_BUG; } } bool string_to_hash_algo(const string & arg, hash_algo & val) { if(strcasecmp(arg.c_str(), "md5") == 0) val = hash_algo::md5; else if(strcasecmp(arg.c_str(), "sha1") == 0) val = hash_algo::sha1; else if(strcasecmp(arg.c_str(), "sha512") == 0) val = hash_algo::sha512; else if(strcasecmp(arg.c_str(), "none") == 0) val = hash_algo::none; else if(strcasecmp(arg.c_str(), "argon2") == 0) val = hash_algo::argon2; else return false; return true; } U_I hash_algo_to_gcrypt_hash(hash_algo algo) { #if CRYPTO_AVAILABLE U_I hash_gcrypt; switch(algo) { case hash_algo::none: throw SRC_BUG; case hash_algo::md5: hash_gcrypt = GCRY_MD_MD5; break; case hash_algo::sha1: hash_gcrypt = GCRY_MD_SHA1; break; case hash_algo::sha512: hash_gcrypt = GCRY_MD_SHA512; break; case hash_algo::argon2: throw SRC_BUG; // not a gcrypt_hash default: throw SRC_BUG; } return hash_gcrypt; #else throw Ecompilation("linking with libgcrypt"); #endif } unsigned char hash_algo_to_char(hash_algo algo) { switch(algo) { case hash_algo::none: return 'n'; case hash_algo::md5: return 'm'; case hash_algo::sha1: return '1'; case hash_algo::sha512: return '5'; case hash_algo::argon2: return 'a'; default: throw SRC_BUG; } } hash_algo char_to_hash_algo(unsigned char arg) { switch(arg) { case '1': return hash_algo::sha1; case '5': return hash_algo::sha512; case 'm': return hash_algo::md5; case 'n': return hash_algo::none; case 'a': return hash_algo::argon2; default: throw Erange("char_to_hash_algo", tools_printf(gettext("unknown hash algorithm corresponding to char `%c'"), arg)); } } } // end of namespace dar-2.7.15/src/libdar/sar.cpp0000644000175000017500000011771014636066467012643 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // this was necessary to compile under Mac OS-X (boggus dirent.h) #if HAVE_STDINT_H #include #endif #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_FCNTL_H #include #endif #if STDC_HEADERS #include #endif } // end extern "C" #include "sar.hpp" #include "deci.hpp" #include "user_interaction.hpp" #include "tools.hpp" #include "erreurs.hpp" #include "cygwin_adapt.hpp" #include "deci.hpp" #include "entrepot.hpp" #include "sar_tools.hpp" #include "fichier_global.hpp" using namespace std; namespace libdar { sar::sar(const shared_ptr & dialog, const string & base_name, const string & extension, const shared_ptr & where, bool by_the_end, const infinint & x_min_digits, bool sequential_read, bool x_lax, const string & execute) : generic_file(gf_read_only), mem_ui(dialog) { opt_warn_overwrite = true; opt_allow_overwrite = false; natural_destruction = true; base = base_name; ext = extension; initial = true; hook = execute; set_info_status(CONTEXT_INIT); slicing.older_sar_than_v8 = false; // will be set to true at header read time a bit further if necessary hash = hash_algo::none; lax = x_lax; min_digits = x_min_digits; seq_read = sequential_read; entr = where; force_perm = false; to_read_ahead = 0; if(seq_read && by_the_end) throw SRC_BUG; // not possible to read sequentially and read by the end at the same time open_file_init(); try { if(!entr) throw SRC_BUG; if(by_the_end) { try { skip_to_eof(); } catch(Erange & e) { string tmp = e.get_message(); get_ui().printf(gettext("Error met while opening the last slice: %S. Trying to open the archive using the first slice..."), &tmp); open_file(1, false); } } else open_file(1, false); } catch(...) { try { close_file(true); } catch(...) { if(of_fd != nullptr) { delete of_fd; of_fd = nullptr; } } throw; } } sar::sar(const shared_ptr & dialog, gf_mode open_mode, const string & base_name, const string & extension, const infinint & file_size, const infinint & first_file_size, bool x_warn_overwrite, bool x_allow_overwrite, const infinint & x_pause, const shared_ptr & where, const label & internal_name, const label & data_name, bool force_permission, U_I permission, hash_algo x_hash, const infinint & x_min_digits, bool format_07_compatible, const string & execute) : generic_file(open_mode), mem_ui(dialog) { if(open_mode == gf_read_only) throw SRC_BUG; if(file_size < header::min_size() + 1) //< one more byte to store at least one byte of data throw Erange("sar::sar", gettext("File size too small")); // note that this test does not warranty that the file is large enough to hold a header structure if(first_file_size < header::min_size() + 1) throw Erange("sar::sar", gettext("First file size too small")); // note that this test does not warranty that the file is large enough to hold a header structure initial = true; lax = false; opt_warn_overwrite = x_warn_overwrite; opt_allow_overwrite = x_allow_overwrite; natural_destruction = true; base = base_name; ext = extension; slicing.other_size = file_size; slicing.first_size = first_file_size; hook = execute; pause = x_pause; hash = x_hash; min_digits = x_min_digits; set_info_status(CONTEXT_OP); of_internal_name = internal_name; of_data_name = data_name; force_perm = force_permission; perm = permission; of_fd = nullptr; of_flag = '\0'; slicing.older_sar_than_v8 = format_07_compatible; to_read_ahead = 0; try { entr = where; if(!entr) throw SRC_BUG; open_file_init(); open_file(1, false); } catch(...) { try { close_file(true); } catch(...) { if(of_fd != nullptr) { delete of_fd; of_fd = nullptr; } } throw; } } void sar::inherited_terminate() { close_file(true); if(get_mode() != gf_read_only && natural_destruction) { set_info_status(CONTEXT_LAST_SLICE); hook_execute(of_current); } } sar::~sar() { try { terminate(); } catch(...) { // ignore all exception } } bool sar::skippable(skippability direction, const infinint & amount) { if(hash != hash_algo::none) return false; switch(direction) { case generic_file::skip_backward: if(of_current == 1) return file_offset - slicing.first_slice_header >= amount; else return file_offset - slicing.other_slice_header >= amount; case generic_file::skip_forward: if(of_current == 1) return (file_offset + amount + (slicing.older_sar_than_v8 ? 0 : 1)) < slicing.first_size; else return (file_offset + amount + (slicing.older_sar_than_v8 ? 0 : 1)) < slicing.other_size; default: throw SRC_BUG; } } bool sar::skip(const infinint & pos) { infinint dest_file, offset; if(is_terminated()) throw SRC_BUG; if(get_position() == pos) return true; // no need to skip to_read_ahead = 0; /////////////////////////// // determination of the file to go and its offset to seek in // slicing.which_slice(pos, dest_file, offset); /////////////////////////// // checking whether the required position is acceptable // if(of_last_file_known && dest_file > of_last_file_num) { // going to EOF open_file(of_last_file_num, true); of_fd->skip_to_eof(); file_offset = of_fd->get_position(); return false; } else { try { open_file(dest_file, false); set_offset(offset); file_offset = offset; return true; } catch(Erange & e) { return false; } } } bool sar::skip_to_eof() { bool ret; if(is_terminated()) throw SRC_BUG; open_last_file(true); if(of_fd == nullptr) throw SRC_BUG; to_read_ahead = 0; ret = of_fd->skip_to_eof(); switch(get_mode()) { case gf_read_only: if(!slicing.older_sar_than_v8) of_fd->skip_relative(-1); file_offset = of_fd->get_position(); set_offset(file_offset); break; case gf_read_write: case gf_write_only: file_offset = of_fd->get_position(); if(of_current == 1) { if(file_offset == slicing.first_size) { // we point to the slice trailer, which is not sar data // so we skip back one byte --file_offset; of_fd->skip(file_offset); } else if(file_offset > slicing.first_size) throw SRC_BUG; // should not be possible to have an initial slice larger than first_size } else { if(file_offset == slicing.other_size) { // we point to the slice trailer, which is not sar data // so we skip back one byte --file_offset; of_fd->skip(file_offset); } else if(file_offset > slicing.other_size) throw SRC_BUG; // should not be possible to have a slice larger than size } break; default: throw SRC_BUG; } return ret; } bool sar::skip_forward(U_I x) { infinint number = of_current; infinint offset = file_offset + x; infinint delta = slicing.older_sar_than_v8 ? 0 : 1; // one byte less per slice with archive format >= 8 if(is_terminated()) throw SRC_BUG; to_read_ahead = 0; while((number == 1 ? offset+delta >= slicing.first_size : offset+delta >= slicing.other_size) && (!of_last_file_known || number <= of_last_file_num)) { offset -= number == 1 ? slicing.first_size - delta : slicing.other_size - delta; offset += slicing.other_slice_header; number++; } if(number == 1 ? offset+delta < slicing.first_size : offset+delta < slicing.other_size) { open_file(number, false); file_offset = offset; set_offset(file_offset); return true; } else return false; } bool sar::skip_backward(U_I x) { infinint number = of_current; infinint offset = file_offset; infinint offset_neg = x; infinint delta = slicing.older_sar_than_v8 ? 0 : 1; // one byte less per slice with archive format >= 8 if(is_terminated()) throw SRC_BUG; while(number > 1 && offset_neg + slicing.other_slice_header > offset) { offset_neg -= offset - slicing.other_slice_header + 1; number--; if(number > 1) offset = slicing.other_size - 1 - delta; else offset = slicing.first_size - 1 - delta; } if((number > 1 ? offset_neg + slicing.other_slice_header : offset_neg + slicing.first_slice_header) <= offset) { open_file(number, true); file_offset = offset - offset_neg; set_offset(file_offset); return true; } else { // seek to beginning of file open_file(1, false); set_offset(slicing.first_slice_header); return false; } } bool sar::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; if(x > 0) return skip_forward(x); if(x < 0) return skip_backward(-x); return true; // when x == 0 } bool sar::truncatable(const infinint & pos) const { infinint dest_file, offset; infinint high_num; // locate the slice and relative offset where to cut slicing.which_slice(pos, dest_file, offset); if(of_last_file_known && of_last_file_num < dest_file) return true; // truncating after EOF if(of_fd == nullptr) throw SRC_BUG; if(dest_file < of_current) return of_fd->truncatable(0); // we test the ability to truncate for // the base objects // but we should also test the ability // to unlink()/delete a file from the entrepot else return of_fd->truncatable(offset); } infinint sar::get_position() const { infinint delta = slicing.older_sar_than_v8 ? 0 : 1; // one byte less per slice with archive format >= 8 if(is_terminated()) throw SRC_BUG; if(of_current > 1) return slicing.first_size - slicing.first_slice_header - delta + (of_current-2)*(slicing.other_size - slicing.other_slice_header - delta) + file_offset - slicing.other_slice_header; else return file_offset - slicing.first_slice_header; } void sar::inherited_read_ahead(const infinint & amount) { infinint avail_in_slice; if(of_current == 1) avail_in_slice = slicing.first_size; else avail_in_slice = slicing.other_size; avail_in_slice -= file_offset; avail_in_slice -= (slicing.older_sar_than_v8 ? 0 : 1); if(avail_in_slice > amount) { if(of_fd != nullptr) of_fd->read_ahead(amount); to_read_ahead = 0; } else { if(of_fd != nullptr) of_fd->read_ahead(avail_in_slice + (slicing.older_sar_than_v8 ? 0 : 1)); to_read_ahead = amount - avail_in_slice; } } U_I sar::inherited_read(char *a, U_I sz) { U_I lu = 0; bool loop = true; while(lu < sz && loop) { U_I tmp; if(of_fd != nullptr) { tmp = of_fd->read(a+lu, sz-lu); if(!slicing.older_sar_than_v8) { // we must avoir reading the terminal flag // when we reach an end of slice if((!size_of_current.is_zero() && of_fd->get_position() == size_of_current) // eof of slice reached || (size_of_current.is_zero() && tmp < sz-lu)) // eof slice in sequential read mode when slice size cannot be know if(tmp > 0) --tmp; // we do not "read" the terminal flag } } else tmp = 0; // simulating an end of slice if(tmp == 0) if(of_flag == flag_type_terminal || get_mode() != gf_read_only) loop = false; else if(is_current_eof_a_normal_end_of_slice()) open_file(of_current + 1, false); else // filling zeroed bytes in place of the missing part of the slice { infinint avail = bytes_still_to_read_in_slice(); U_I place = sz-lu; if(avail < place) { place = 0; avail.unstack(place); } (void)memset(a+lu, 0, place); lu += place; file_offset += place; } else { lu += tmp; file_offset += tmp; } } return lu; } void sar::inherited_truncate(const infinint & pos) { infinint dest_file, offset; infinint high_num; // locate the slice and relative offset where to cut slicing.which_slice(pos, dest_file, offset); if(of_last_file_known && of_last_file_num < dest_file) return; // nothing to do // must skip backward if we are in a slice following // the truncate position because we cannot keep the current slice opened if(pos < get_position()) skip(pos); if(dest_file < of_current) throw SRC_BUG; // the previous skip() should have avoided this situation if(dest_file > of_current) return; // truncating after eof, inherited_truncate should not expand a file // truncating the current slice to offset if(of_fd == nullptr) throw SRC_BUG; of_fd->truncate(offset); // removing slices after the current slice sar_tools_remove_higher_slices_than(*entr, base, min_digits, ext, of_current, get_ui()); } void sar::inherited_write(const char *a, U_I to_write) { infinint max_at_once; U_I tmp_wrote; U_I trailer_size = slicing.older_sar_than_v8 ? 0 : 1; to_read_ahead = 0; while(to_write > 0) { max_at_once = of_current == 1 ? (slicing.first_size - file_offset) - trailer_size : (slicing.other_size - file_offset) - trailer_size; tmp_wrote = 0; max_at_once.unstack(tmp_wrote); if(tmp_wrote > to_write) tmp_wrote = to_write; if(tmp_wrote > 0) { of_fd->write(a, tmp_wrote); to_write -= tmp_wrote; file_offset += tmp_wrote; a += tmp_wrote; } else { open_file(of_current + 1, false); continue; } } } void sar::close_file(bool terminal) { if(of_fd != nullptr) { char flag = terminal ? flag_type_terminal : flag_type_non_terminal; if(get_mode() == gf_read_write || get_mode() == gf_write_only) { if(slicing.older_sar_than_v8) { header h = make_write_header(of_current, terminal ? flag_type_terminal : flag_type_non_terminal); of_fd->skip(0); h.write(get_ui(), *of_fd); } else { if(terminal) { if(!of_fd->skip_to_eof()) throw SRC_BUG; } else { if(!of_fd->skip((of_current > 1 ? slicing.other_size : slicing.first_size) - 1)) throw SRC_BUG; // cannot skip at end of slice } if(of_fd->get_position() > (of_current > 1 ? slicing.other_size : slicing.first_size) - 1) throw SRC_BUG; of_fd->write(&flag, 1); } } of_fd->terminate(); delete of_fd; of_fd = nullptr; } } void sar::open_readonly(const string & fic, const infinint &num, bool bytheend) { header h; while(of_fd == nullptr) { // launching user command if any hook_execute(num); // trying to open the file // try { of_fd = entr->open(get_pointer(), fic, gf_read_only, false, //< force permission 0, //< permission to enforce (not used here) false, //< fail if exists false, //< erase hash_algo::none, !seq_read); if(of_fd == nullptr) throw SRC_BUG; if(!seq_read) { try { of_fd->fadvise(fichier_global::advise_normal); // we have no advise to give to the system when reading a slice } catch(Erange & e) { // we silently ignore fadvise error // this is not crucial } } size_of_current = of_fd->get_size(); } catch(Euser_abort & e) { if(lax) { get_ui().message(string(gettext("LAX MODE: Caught exception: "))+ e.get_message()); get_ui().pause(tools_printf(gettext("LAX MODE: %S is missing, You have the possibility to create a zero byte length file under the name of this slice, to replace this missing file. This will of course generate error messages about the information that is missing in this slice, but at least libdar will be able to continue. Can we continue now?"), &fic)); continue; // we restart the while loop } else throw; } catch(Esystem & e) { switch(e.get_code()) { case Esystem::io_absent: if(!lax) get_ui().pause(tools_printf(gettext("%S is required for further operation, please provide the file."), &fic)); else get_ui().pause(tools_printf(gettext("%S is required for further operation, please provide the file if you have it."), &fic)); break; case Esystem::io_exist: throw SRC_BUG; case Esystem::io_access: e.prepend_message(tools_printf(gettext("Failed reading slice %S: "), &fic)); throw; // propagate the exception case Esystem::io_ro_fs: throw SRC_BUG; default: throw SRC_BUG; } continue; // we restart the while loop } // trying to read the header // try { h.read(get_ui(), *of_fd, lax); } catch(Ethread_cancel & e) { throw; } catch(Euser_abort & e) { throw; } catch(Efeature & e) { throw; } catch(Elimitint & e) { throw; } catch(Ebug & e) { throw; } catch(Egeneric & e) { if(!lax) { close_file(false); get_ui().pause(tools_printf(gettext("%S has a bad or corrupted header, please provide the correct file."), &fic)); continue; } else get_ui().message(tools_printf(gettext("LAX MODE: %S has a bad or corrupted header, trying to guess original values and continuing if possible"), &fic)); } // checking against the magic number // if(h.get_set_magic() != SAUV_MAGIC_NUMBER) { if(!lax) { close_file(false); get_ui().pause(tools_printf(gettext("%S is not a valid file (wrong magic number), please provide the good file."), &fic)); continue; } else get_ui().message(tools_printf(gettext("LAX MODE: In spite of its name, %S does not appear to be a dar slice, assuming a data corruption took place and continuing"), &fic)); } if(h.is_old_header() && slicing.first_slice_header.is_zero() && num != 1) throw Erange("sar::open_readonly", gettext("This is an old archive, it can only be opened starting by the first slice")); // checking the ownership of the set of file (= slice of the same archive or not) // if(slicing.first_slice_header.is_zero()) // this is the first time we open a slice for this archive, we don't even know the slices size { of_internal_name = h.get_set_internal_name(); of_data_name = h.get_set_data_name(); try { if(!h.get_slice_size(slicing.other_size)) { if(!lax) throw SRC_BUG; // slice size should be known or determined by header class else slicing.other_size = 0; } if(!h.get_first_slice_size(slicing.first_size)) slicing.first_size = slicing.other_size; if(lax && (slicing.first_size.is_zero() || slicing.other_size.is_zero())) { try { infinint tmp_num = 0; string answ; get_ui().pause(gettext("LAX MODE: Due to probable data corruption, dar could not determine the correct size of slices in this archive. For recent archive, this information is duplicated in each slice, do you want to try opening another slice to get this value if present?")); do { answ = get_ui().get_string(gettext("LAX MODE: Please provide the slice number to read: "), true); try { deci tmp = answ; tmp_num = tmp.computer(); } catch(Edeci &e) { get_ui().message(gettext("LAX MODE: Please provide an strictly positive integer number")); tmp_num = 0; } } while(tmp_num.is_zero()); get_ui().printf(gettext("LAX MODE: opening slice %i to read its slice header"), &tmp_num); open_file(tmp_num, false); get_ui().printf(gettext("LAX MODE: closing slice %i, header properly fetched"), &tmp_num); close_file(false); continue; } catch(Euser_abort & e) { get_ui().message(gettext("LAX MODE: In spite of a the absence of a known slice size, continuing anyway")); } } slicing.first_slice_header = of_fd->get_position(); slicing.other_slice_header = h.is_old_header() ? header::min_size() : slicing.first_slice_header; if(slicing.first_slice_header >= slicing.first_size && !lax) { if(! seq_read || ! slicing.first_size.is_zero() || slicing.first_size != slicing.other_size) throw Erange("sar::sar", gettext("Incoherent slice header: First slice size too small")); } if(slicing.other_slice_header >= slicing.other_size && !lax) { if( ! seq_read || ! slicing.other_size.is_zero()) throw Erange("sar::sar", gettext("incoherent slice header: Slice size too small")); } slicing.older_sar_than_v8 = h.is_old_header(); } catch(Erange & e) { close_file(false); get_ui().pause(tools_printf(gettext("Error opening %S : "), &fic) + e.get_message() + gettext(" . Retry ?")); continue; } } else { if(of_internal_name != h.get_set_internal_name()) { if(!lax) { close_file(false); get_ui().pause(fic + gettext(" is a slice from another backup, please provide the correct slice.")); continue; } else { get_ui().message(gettext("LAX MODE: internal name of the slice leads dar to consider it is not member of the same archive. Assuming data corruption occurred and relying on the filename of this slice as proof of its membership to the archive")); } } } // checking the flag // if(h.get_set_flag() == flag_type_located_at_end_of_slice) { infinint current_pos = of_fd->get_position(); char end_flag; if(!seq_read) { of_fd->skip_to_eof(); of_fd->skip_relative(-1); of_fd->read(&end_flag, 1); // reading the last char of the slice if(bytheend) of_fd->skip_relative(-1); else of_fd->skip(current_pos); switch(end_flag) { case flag_type_terminal: case flag_type_non_terminal: h.get_set_flag() = end_flag; break; case flag_type_located_at_end_of_slice: if(!lax) throw Erange("sar::open_readonly", gettext("Data corruption met at end of slice, forbidden flag found at this position")); else h.get_set_flag() = end_flag; break; default: if(!lax) throw Erange("sar::open_readonly", gettext("Data corruption met at end of slice, unknown flag found")); else h.get_set_flag() = end_flag; break; } } else { h.get_set_flag() = flag_type_non_terminal; // to pass the following sanity checks // and be coherent (we do not know the // wether this is the last slice of the // backup or not at this point in time } } else // old slice without flag at end of slice { if(bytheend) of_fd->skip_to_eof(); } switch(h.get_set_flag()) { case flag_type_terminal: if(of_last_file_known) { if(of_last_file_num != num) { if(!lax) throw Erange("sar::open_readonly", tools_printf(gettext("Two different slices (%i and %i) are marked as the last slice of the backup!"), &of_last_file_num, &num)); else { get_ui().message(tools_printf(gettext("LAX MODE: slices %i and %i are both recorded as last slice of the archive, keeping the higher number as the real last slice"), &of_last_file_num, &num)); if(num > of_last_file_num) { of_last_file_num = num; of_last_file_size = of_fd->get_size(); } } } // else nothing to do. } else { of_last_file_known = true; of_last_file_num = num; of_last_file_size = of_fd->get_size(); } break; case flag_type_non_terminal: break; default : if(!lax) { close_file(false); get_ui().pause(tools_printf(gettext("Slice %S has an unknown flag (neither terminal nor non_terminal file)."), &fic)); continue; } else if(of_max_seen <= num) { string answ; do { answ = get_ui().get_string(tools_printf(gettext("Due to data corruption, it is not possible to know if slice %S is the last slice of the archive or not. I need your help to figure out this. At the following prompt please answer either one of the following words: \"last\" or \"notlast\" according to the nature of this slice (you can also answer with \"abort\" to abort the program immediately): "), &fic), true); } while(answ != gettext("last") && answ != gettext("notlast") && answ != gettext("abort")); if(answ == gettext("abort")) throw Euser_abort("LAX MODE: Help the compression used..."); if(answ == gettext("last")) { of_last_file_known = true; of_last_file_num = num; of_last_file_size = of_fd->get_size(); h.get_set_flag() = flag_type_terminal; } else h.get_set_flag() = flag_type_non_terminal; } else { get_ui().message(gettext("LAX MODE: Slice flag corrupted, but a slice of higher number has been seen, thus the header flag was surely not indicating this slice as the last of the archive. Continuing")); h.get_set_flag() = flag_type_non_terminal; } } of_flag = h.get_set_flag(); if(lax) { infinint tmp; if(!h.get_slice_size(tmp) || tmp.is_zero()) { // a problem occured while reading slice header, however we know what is its expected size // so we seek the next read to the end of the slice header if(num == 1) of_fd->skip(slicing.first_slice_header); else of_fd->skip(slicing.other_slice_header); } } } } void sar::open_writeonly(const string & fic, const infinint &num, bool bytheend) { bool unlink_on_error = false; bool do_erase = false; // open for writing but succeeds only if this file does NOT already exist try { try { of_fd = entr->open(get_pointer(), fic, hash == hash_algo::none ? gf_read_write : gf_write_only, // yes, no anymore always writeonly as stated in the name of this method force_perm, perm, true, //< fail_if_exists false, //< erase hash); } catch(Erange & e) { string tmp = e.get_message(); get_ui().message(tools_printf(gettext("failed openning slice %S: %S. Will try to erase it first, if allowed"), &fic, &tmp)); throw Esystem("sar::open_writeonly", "failed openning, will try erasing first", Esystem::io_exist); } } catch(Esystem & e) { switch(e.get_code()) { case Esystem::io_exist: try { try { // the file exists, re-trying opening it without fail_if_exists of_fd = entr->open(get_pointer(), fic, gf_read_only, false, //< force permission 0, //< permission to enforce (not used here) false, //< fail if exists false, //< erase hash_algo::none); if(of_fd == nullptr) throw SRC_BUG; try { header h; try { h.read(get_ui(), *of_fd); } catch(Erange & e) { h.get_set_internal_name() = of_internal_name; h.get_set_internal_name().invert_first_byte(); // this way we are sure that the file is not considered as part of the current SAR } if(h.get_set_internal_name() != of_internal_name) do_erase = true; // this is not a slice of the current archive delete of_fd; of_fd = nullptr; } catch(...) { if(of_fd != nullptr) { delete of_fd; of_fd = nullptr; } throw; } } catch(Esystem & f) { switch(f.get_code()) { case Esystem::io_exist: throw SRC_BUG; case Esystem::io_absent: throw SRC_BUG; case Esystem::io_access: case Esystem::io_ro_fs: e.prepend_message(tools_printf(gettext("Failed creating slice %S: "), &fic)); throw; default: throw SRC_BUG; } } } catch(Ebug & e) { throw; } catch(...) { do_erase = true; // reading failed, trying overwriting (if allowed) } try { if(do_erase) { if(!opt_allow_overwrite) throw Erange("sar::open_writeonly", gettext("file exists, and DONT_ERASE option is set.")); if(opt_warn_overwrite) { try { get_ui().pause(fic + gettext(" is about to be overwritten.")); unlink_on_error = true; } catch(...) { natural_destruction = false; throw; } } else unlink_on_error = true; // open with overwriting of_fd = entr->open(get_pointer(), fic, hash == hash_algo::none ? gf_read_write : gf_write_only, // yes, no more write only as stated in the name of this method force_perm, perm, false, //< fail if exists true, //< erase hash); } else // open without overwriting if(hash == hash_algo::none) of_fd = entr->open(get_pointer(), fic, hash == hash_algo::none ? gf_read_write : gf_write_only, // yes, no more write only as stated in the name of this method force_perm, perm, false, //< fail if exists false, //< erase hash); else throw SRC_BUG; // cannot calculate a hash on a just openned file that is not empty } catch(Esystem & e) { switch(e.get_code()) { case Esystem::io_exist: throw SRC_BUG; // not called with fail_if_exists set case Esystem::io_absent: throw SRC_BUG; // not called in read mode case Esystem::io_access: case Esystem::io_ro_fs: e.prepend_message(tools_printf(gettext("Failed creating slice %S: "), &fic)); throw; // propagate the exception default: throw SRC_BUG; } } break; case Esystem::io_absent: throw SRC_BUG; case Esystem::io_access: case Esystem::io_ro_fs: e.prepend_message(tools_printf(gettext("Failed creating slice %S: "), &fic)); throw; // propagate the exception default: throw SRC_BUG; } } if(of_fd == nullptr) throw SRC_BUG; try { header h; of_flag = flag_type_located_at_end_of_slice; h = make_write_header(num, of_flag); h.write(get_ui(), *of_fd); if(num == 1) { slicing.first_slice_header = of_fd->get_position(); if(slicing.first_slice_header.is_zero()) throw SRC_BUG; slicing.other_slice_header = slicing.first_slice_header; // same header in all slice since release 2.4.0 if(slicing.first_slice_header >= slicing.first_size) throw Erange("sar::sar", gettext("First slice size is too small to even just be able to drop the slice header")); if(slicing.other_slice_header >= slicing.other_size) throw Erange("sar::sar", gettext("Slice size is too small to even just be able to drop the slice header")); size_of_current = slicing.first_size; } else size_of_current = slicing.other_size; } catch(...) { if(unlink_on_error) entr->unlink(fic); if(of_fd != nullptr) { delete of_fd; of_fd = nullptr; } throw; } if(bytheend) of_fd->skip_to_eof(); } void sar::open_file_init() { of_max_seen = 0; of_last_file_known = false; of_fd = nullptr; of_flag = '\0'; slicing.first_slice_header = 0; // means that the sizes have to be determined from file or wrote to file slicing.other_slice_header = 0; size_of_current = 0; // not used in write mode } void sar::open_file(infinint num, bool bytheend) { if(of_fd == nullptr || of_current != num) { const string display = sar_tools_make_filename(base, num, min_digits, ext); switch(get_mode()) { case gf_read_only: close_file(false); open_readonly(display, num, bytheend); break; case gf_write_only: case gf_read_write: // adding the trailing flag if(of_fd != nullptr) close_file(false); if(!initial) { // launch the shell command after the slice has been written hook_execute(of_current); if(!pause.is_zero() && (((num-1) % pause).is_zero())) { deci conv = of_current; bool ready = false; while(!ready) { try { get_ui().pause(string(gettext("Finished writing to file ")) + conv.human() + gettext(", ready to continue ? ")); ready = true; } catch(Euser_abort & e) { get_ui().message(string(gettext("If you really want to abort the archive creation hit CTRL-C, then press enter."))); ready = false; } } } } else initial = false; open_writeonly(display, num, bytheend); break; default : close_file(false); throw SRC_BUG; } if(of_max_seen < num) of_max_seen = num; file_offset = num == 1 ? slicing.first_slice_header : slicing.other_slice_header; if(num == of_current + 1 && !to_read_ahead.is_zero()) { of_current = num; inherited_read_ahead(to_read_ahead); } else of_current = num; } } void sar::set_offset(infinint offset) { if(of_fd == nullptr) throw Erange("sar::set_offset", gettext("file not open")); else { if(!of_fd->skip(offset)) throw Erange("sar::set_offset","Cannot seek to the requested position"); } } void sar::open_last_file(bool bytheend) { infinint num; switch(get_mode()) { case gf_read_only: if(of_last_file_known) open_file(of_last_file_num, bytheend); else // last slice number is not known { bool ask_user = false; while(of_fd == nullptr || of_flag != flag_type_terminal) { if(sar_tools_get_higher_number_in_dir(get_ui(), *entr, base, min_digits, ext, num)) { open_file(num, bytheend); if(of_flag != flag_type_terminal) { if(!ask_user) { close_file(false); hook_execute(0); // %n replaced by 0 means last file is about to be requested ask_user = true; } else { close_file(false); get_ui().pause(string(gettext("The last file of the set is not present in ")) + entr->get_url() + gettext(" , please provide it.")); } } } else // not slice available in the directory if(!ask_user) { hook_execute(0); // %n replaced by 0 means last file is about to be requested ask_user = true; } else { string chem = entr->get_url(); close_file(false); get_ui().pause(tools_printf(gettext("No backup file is present in %S for archive %S, please provide the last file of the set."), &chem, &base)); } } } break; case gf_read_write: case gf_write_only: open_file(of_max_seen, bytheend); break; default: throw SRC_BUG; } } header sar::make_write_header(const infinint & num, char flag) { header hh; hh.get_set_magic() = SAUV_MAGIC_NUMBER; hh.get_set_internal_name() = of_internal_name; hh.get_set_data_name() = of_data_name; hh.get_set_flag() = flag; if(slicing.older_sar_than_v8) { if(num == 1) { hh.set_slice_size(slicing.other_size); if(slicing.other_size != slicing.first_size) hh.set_first_slice_size(slicing.first_size); } hh.set_format_07_compatibility(); } else { hh.set_slice_size(slicing.other_size); if(slicing.other_size != slicing.first_size) hh.set_first_slice_size(slicing.first_size); } return hh; } void sar::hook_execute(const infinint &num) { if(hook != "" && natural_destruction) { try { deci conv = num; string num_str = conv.human(); if(!entr) throw SRC_BUG; tools_hook_substitute_and_execute(get_ui(), hook, entr->get_full_path().display(), base, num_str, sar_tools_make_padded_number(num_str, min_digits), ext, get_info_status(), entr->get_url()); } catch(Escript & g) { natural_destruction = false; throw; } } } bool sar::is_current_eof_a_normal_end_of_slice() const { infinint delta = slicing.older_sar_than_v8 ? 0 : 1; // one byte less per slice with archive format >= 8 if(of_last_file_known && of_last_file_num == of_current) // we are in the last slice, thus eof may occur at any place return true; // we are not in the last slice, thus we can determine at which offset the eof must be met for this slice if(of_current == 1) return file_offset >= slicing.first_size - delta; else return file_offset >= slicing.other_size - delta; } infinint sar::bytes_still_to_read_in_slice() const { infinint delta = slicing.older_sar_than_v8 ? 0 : 1; // one byte less per slice with archive format >= 8 if(of_last_file_known && of_last_file_num == of_current) throw SRC_BUG; // cannot figure out the expected slice size of the last slice of the archive if(of_current == 1) if(file_offset > slicing.first_size - delta) return 0; else return slicing.first_size - file_offset - delta; else if(file_offset > slicing.other_size - delta) return 0; else return slicing.other_size - file_offset - delta; } } // end of namespace dar-2.7.15/src/libdar/header_flags.hpp0000644000175000017500000000621014636066467014457 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file header_flags.hpp /// \brief archive header flag field management /// \ingroup Private #ifndef HEADER_FLAGS_HPP #define HEADER_FLAGS_HPP #include "../my_config.h" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// manages the flag field used for now in the archive header /// \note historically flags were used a usual constant bitfield expression /// when 7 different bits were used came the question about adding more than one /// bit in the future. This last bit (0x01) has been reserved to flag that the /// bit field had a extra byte to take into account. By extension, the 0x0100 bit /// has been reserved to scale beyond 2 bytes or 14 different possible bits. /// In consequence, you cannot set or unset a bitfield having one of its byte /// with its least significant bit set (0x01, 0x0100, 0x1000,...). /// the use a meaning of each bit is left outside of this class to ease this /// class reuse in other context if the need arises in the future. class header_flags { public: header_flags(): bits(0) {}; header_flags(generic_file & f) { read(f); }; header_flags(const header_flags & ref) = default; header_flags(header_flags && ref) noexcept = default; header_flags & operator = (const header_flags & ref) = default; header_flags & operator = (header_flags && ref) noexcept = default; ~header_flags() = default; /// add all the bits set to 1 in the argument void set_bits(U_I bitfield); /// remove all the bits set to in in the argument void unset_bits(U_I bitfield); /// return true if *all* bits of the argument set to 1, are set in this header_flags bool is_set(U_I bitfield) const; /// set the header_flags from a generic_file void read(generic_file & f); /// dump the header_flags to generic_file void dump(generic_file & f) const; /// clear all flags void clear() { bits = 0; }; /// whether all bits are cleared bool is_all_cleared() { return bits == 0; }; private: U_I bits; ///< future implementation could rely on infinint for a arbitrary large bitfield static bool has_an_lsb_set(U_I bitfield); }; } // end of namespace #endif dar-2.7.15/src/libdar/crc.cpp0000644000175000017500000002672114636066467012626 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif } // end extern "C" #include #include #include "crc.hpp" using namespace std; #define INFININT_MODE_START 10240 namespace libdar { static void n_compute(const char *buffer, U_I length, unsigned char * begin, unsigned char * & pointer, unsigned char * end, U_I crc_size); ///////////////////////////////////////////// // some TEMPLATES and static routines first // template string T_crc2str(P begin, P end) { ostringstream ret; P curs = begin; while(curs != end) { ret << hex << ((*curs & 0xF0) >> 4); ret << hex << (*curs & 0x0F); ++curs; } return ret.str(); } template void T_old_read(P & pointer, P begin, P end, const char *buffer, U_I size) { U_I pos = 0; while(pointer != end && pos < size) { *pointer = buffer[pos]; ++pointer; ++pos; } if(pointer != end || pos < size) throw SRC_BUG; // should reach both ends at the same time pointer = begin; } template void B_compute_block(B anonymous, const char *buffer, U_I length, unsigned char * begin, unsigned char * & pointer, unsigned char * end, U_I & cursor) { B *buf_end = (B *)(buffer + length - sizeof(anonymous) + 1); B *buf_ptr = (B *)(buffer); B *crc_end = (B *)(end); B *crc_ptr = (B *)(begin); if(begin >= end) throw SRC_BUG; else { U_I crc_size = end - begin; if(crc_size % sizeof(anonymous) != 0) throw SRC_BUG; if(crc_size / sizeof(anonymous) == 0) throw SRC_BUG; } while(buf_ptr < buf_end) { *crc_ptr ^= *buf_ptr; ++buf_ptr; ++crc_ptr; if(crc_ptr >= crc_end) crc_ptr = (B *)(begin); } cursor = (char *)(buf_ptr) - buffer; pointer = (unsigned char *)(crc_ptr); } template void T_compute(const char *buffer, U_I length, P begin, P & pointer, P end) { if(pointer == end) throw SRC_BUG; for(U_I cursor = 0; cursor < length; ++cursor) { *pointer ^= buffer[cursor]; if(++pointer == end) pointer = begin; } } static void n_compute(const char *buffer, U_I length, unsigned char * begin, unsigned char * & pointer, unsigned char * end, U_I crc_size) { U_I cursor = 0; //< index of next byte to read from buffer // initial bytes if(pointer != begin) { while(pointer != end && cursor < length) { *pointer ^= buffer[cursor]; ++cursor; ++pointer; } if(pointer == end) // we had enough data to have pointer reach the end of the crc_field pointer = begin; } // block bytes if(pointer == begin && cursor < length) // we can now use the optimized rountine relying on operation by block of bytes { U_I partial_cursor = 0; // But we cannot use optimized method on some systems if we are not aligned to the size boundary if(crc_size % 8 == 0 && (U_I)(buffer + cursor) % 8 == 0) B_compute_block(U_64(0), buffer + cursor, length - cursor, begin, pointer, end, partial_cursor); else if(crc_size % 4 == 0 && (U_I)(buffer + cursor) % 4 == 0) B_compute_block(U_32(0), buffer + cursor, length - cursor, begin, pointer, end, partial_cursor); else if(crc_size % 2 == 0 && (U_I)(buffer + cursor) % 2 == 0) B_compute_block(U_16(0), buffer + cursor, length - cursor, begin, pointer, end, partial_cursor); /// warning, adding a new type here need modifying crc_n::alloc() to provide aligned crc storage cursor += partial_cursor; } // final bytes if(cursor < length) T_compute(buffer + cursor, length - cursor, begin, pointer, end); } template bool T_compare(P me_begin, P me_end, P you_begin, P you_end) { P me = me_begin; P you = you_begin; while(me != me_end && you != you_end && *me == *you) { ++me; ++you; } return me == me_end && you == you_end; } ///////////////////////////////////////////// // Class CRC_I implementation follows // crc_i::crc_i(const infinint & width) : size(width), cyclic(width) { if(width.is_zero()) throw Erange("crc::crc", gettext("Invalid size for CRC width")); clear(); } crc_i::crc_i(const infinint & width, proto_generic_file & f) : size(width), cyclic(f, width) { pointer = cyclic.begin(); } bool crc_i::operator == (const crc & ref) const { const crc_i *ref_i = dynamic_cast(&ref); if(ref_i == nullptr) throw SRC_BUG; if(size != ref_i->size) return false; else // same size return T_compare(cyclic.begin(), cyclic.end(), ref_i->cyclic.begin(), ref_i->cyclic.end()); } void crc_i::compute(const infinint & offset, const char *buffer, U_I length) { infinint tmp = offset % size; // first we skip the cyclic at the correct position pointer.skip_to(cyclic, tmp); // now we can compute the CRC compute(buffer, length); } void crc_i::compute(const char *buffer, U_I length) { T_compute(buffer, length, cyclic.begin(), pointer, cyclic.end()); } void crc_i::clear() { cyclic.clear(); pointer = cyclic.begin(); } void crc_i::dump(proto_generic_file & f) const { size.dump(f); cyclic.dump(f); } string crc_i::crc2str() const { return T_crc2str(cyclic.begin(), cyclic.end()); } void crc_i::copy_from(const crc_i & ref) { if(size != ref.size) { size = ref.size; cyclic = ref.cyclic; } else copy_data_from(ref); pointer = cyclic.begin(); } void crc_i::copy_data_from(const crc_i & ref) { if(ref.size == size) { storage::iterator ref_it = ref.cyclic.begin(); storage::iterator it = cyclic.begin(); while(ref_it != ref.cyclic.end() && it != cyclic.end()) { *it = *ref_it; ++it; ++ref_it; } if(ref_it != ref.cyclic.end() || it != cyclic.end()) throw SRC_BUG; } else throw SRC_BUG; } ///////////////////////////////////////////// // Class CRC_N implementation follows // crc_n::crc_n(U_I width) { pointer = nullptr; cyclic = nullptr; try { if(width == 0) throw Erange("crc::crc", gettext("Invalid size for CRC width")); alloc(width); clear(); } catch(...) { destroy(); throw; } } crc_n::crc_n(U_I width, proto_generic_file & f) { pointer = nullptr; cyclic = nullptr; try { alloc(width); f.read((char*)cyclic, size); } catch(...) { destroy(); throw; } } crc_n & crc_n::operator = (const crc_n & ref) { if(size != ref.size) { destroy(); copy_from(ref); } else copy_data_from(ref); return *this; } bool crc_n::operator == (const crc & ref) const { const crc_n *ref_n = dynamic_cast(&ref); if(ref_n == nullptr) throw SRC_BUG; if(size != ref_n->size) return false; else // same size return T_compare(cyclic, cyclic + size, ref_n->cyclic, ref_n->cyclic + ref_n->size); } void crc_n::compute(const infinint & offset, const char *buffer, U_I length) { infinint tmp = offset % size; U_I s_offset = 0; // first we skip the cyclic at the correct position tmp.unstack(s_offset); if(tmp != 0) throw SRC_BUG; // tmp does not fit in a U_I variable ! pointer = cyclic + s_offset; // now we can compute the CRC compute(buffer, length); } void crc_n::compute(const char *buffer, U_I length) { n_compute(buffer, length, cyclic, pointer, cyclic + size, size); } void crc_n::clear() { (void)memset(cyclic, 0, size); pointer = cyclic; } void crc_n::dump(proto_generic_file & f) const { infinint tmp = size; tmp.dump(f); f.write((const char *)cyclic, size); } string crc_n::crc2str() const { return T_crc2str(cyclic, cyclic + size); } void crc_n::alloc(U_I width) { size = width; ////////////////////////////////////////////////////////////////////// // the following trick is to have cyclic aligned at its boundary size // (its allocated address is a multiple of it size) // some CPU need that (sparc), and it does not hurt for other ones. if(width % 8 == 0) cyclic = (unsigned char *)(new (nothrow) U_64[width/8]); else if(width % 4 == 0) cyclic = (unsigned char *)(new (nothrow) U_32[width/4]); else if(width % 2 == 0) cyclic = (unsigned char *)(new (nothrow) U_16[width/2]); else cyclic = new (nothrow) unsigned char[size]; // end of the trick and back to default situation ////////////////////////////////////////////////////////////////////// // WARNING! this trick allows the use of 2, 4 or 8 bytes operations // // instead of byte by byte one, in n_compute calls B_compute_block // // CODE MUST BE ADAPTED THERE AND IN destroy() IF CHANGED HERE!!! // ////////////////////////////////////////////////////////////////////// if(cyclic == nullptr) throw Ememory("crc::copy_from"); pointer = cyclic; } void crc_n::copy_from(const crc_n & ref) { alloc(ref.size); copy_data_from(ref); } void crc_n::copy_data_from(const crc_n & ref) { if(size != ref.size) throw SRC_BUG; (void)memcpy(cyclic, ref.cyclic, size); pointer = cyclic; } void crc_n::destroy() { if(cyclic != nullptr) { delete [] cyclic; cyclic = nullptr; } size = 0; pointer = nullptr; } ///////////////////////////////////////////// // exported routines implementation // crc *create_crc_from_file(proto_generic_file & f, bool old) { crc *ret = nullptr; if(old) ret = new (nothrow) crc_n(crc::OLD_CRC_SIZE, f); else { infinint taille = f; // reading the crc size if(taille < INFININT_MODE_START) { U_I s = 0; taille.unstack(s); if(!taille.is_zero()) throw SRC_BUG; ret = new (nothrow) crc_n(s, f); } else ret = new (nothrow) crc_i(taille, f); } if(ret == nullptr) throw Ememory("create_crc_from_file"); return ret; } crc *create_crc_from_size(infinint width) { crc *ret = nullptr; if(width < INFININT_MODE_START) { U_I s = 0; width.unstack(s); if(!width.is_zero()) throw SRC_BUG; ret = new (nothrow) crc_n(s); } else ret = new (nothrow) crc_i(width); if(ret == nullptr) throw Ememory("create_crc_from_size"); return ret; } } // end of namespace dar-2.7.15/src/libdar/cat_detruit.cpp0000644000175000017500000000437214636066467014364 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_detruit.hpp" using namespace std; namespace libdar { cat_detruit::cat_detruit(const smart_pointer & pdesc, const archive_version & reading_ver, bool small) : cat_nomme(pdesc, small, saved_status::saved) { generic_file *ptr = nullptr; pdesc->check(small); if(small) ptr = pdesc->esc; else ptr = pdesc->stack; if(ptr->read((char *)&signe, 1) != 1) throw Erange("cat_detruit::cat_detruit", gettext("missing data to build")); if(reading_ver > 7) del_date.read(*ptr, reading_ver); else del_date = datetime(0); } bool cat_detruit::operator == (const cat_entree & ref) const { const cat_detruit *ref_det = dynamic_cast(&ref); if(ref_det == nullptr) return false; else return signe == ref_det->signe && del_date == ref_det->del_date && cat_nomme::operator == (ref); } void cat_detruit::inherited_dump(const pile_descriptor & pdesc, bool small) const { generic_file * ptr = nullptr; cat_nomme::inherited_dump(pdesc, small); pdesc.check(small); if(small) ptr = pdesc.esc; else ptr = pdesc.stack; ptr->write((char *)&signe, 1); del_date.dump(*ptr); } } // end of namespace dar-2.7.15/src/libdar/archive_summary.hpp0000644000175000017500000001326014636066467015254 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive_summary.hpp /// \brief datastructure returned by archive::summary_data /// \ingroup API #ifndef ARCHIVE_SUMMARY_HPP #define ARCHIVE_SUMMARY_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "entree_stats.hpp" namespace libdar { /// \addtogroup API /// @{ /// the archive_summary class provides information about a given archive class archive_summary { public: archive_summary() { clear(); }; archive_summary(const archive_summary & ref) = default; archive_summary(archive_summary && ref) noexcept = default; archive_summary & operator = (const archive_summary & ref) = default; archive_summary & operator = (archive_summary && ref) noexcept = default; ~archive_summary() = default; // GETTINGS const infinint & get_slice_size() const { return slice_size; }; const infinint & get_first_slice_size() const { return first_slice_size; }; const infinint & get_last_slice_size() const { return last_slice_size; }; const infinint & get_ref_slice_size() const { return ref_slice_size; }; const infinint & get_ref_first_slice_size() const { return ref_first_slice_size; }; const infinint & get_slice_number() const { return slice_number; }; const infinint & get_archive_size() const { return archive_size; }; const infinint & get_catalog_size() const { return catalog_size; }; const infinint & get_storage_size() const { return storage_size; }; const infinint & get_data_size() const { return data_size; }; const entree_stats & get_contents() const { return contents; }; std::string get_edition() const { return edition; }; std::string get_compression_algo() const { return algo_zip; }; std::string get_user_comment() const { return user_comment; }; std::string get_cipher() const { return cipher; }; std::string get_asym() const { return asym; }; bool get_signed() const { return is_signed; }; bool get_tape_marks() const { return tape_marks; }; std::string get_in_place() const { return in_place; }; // SETTINGS void set_slice_size(const infinint & arg) { slice_size = arg; }; void set_first_slice_size(const infinint & arg) { first_slice_size = arg; }; void set_last_slice_size(const infinint & arg) { last_slice_size = arg; }; void set_ref_slice_size(const infinint & arg) { ref_slice_size = arg; }; void set_ref_first_slice_size(const infinint & arg) { ref_first_slice_size = arg; }; void set_slice_number(const infinint & arg) { slice_number = arg; }; void set_archive_size(const infinint & arg) { archive_size = arg; }; void set_catalog_size(const infinint & arg) { catalog_size = arg; }; void set_storage_size(const infinint & arg) { storage_size = arg; }; void set_data_size(const infinint & arg) { data_size = arg; }; void set_contents(const entree_stats & arg) { contents = arg; }; void set_edition(const std::string & arg) { edition = arg; }; void set_compression_algo(const std::string & arg) { algo_zip = arg; }; void set_user_comment(const std::string & arg) { user_comment = arg; }; void set_cipher(const std::string & arg) { cipher = arg; }; void set_asym(const std::string & arg) { asym = arg; }; void set_signed(bool arg) { is_signed = arg; }; void set_tape_marks(bool arg) { tape_marks = arg; }; void set_in_place(const std::string & arg) { in_place = arg; }; void clear(); private: infinint slice_size; ///< slice of the middle slice or zero if not applicable infinint first_slice_size; ///< slice of the first slices or zero if not applicable infinint last_slice_size; ///< slice of the last slice or zero if not applicable infinint ref_slice_size; ///< slice of the slice of the archive of reference infinint ref_first_slice_size;///< slice of the first slice of the archive of reference infinint slice_number; ///< number of slices composing the archive of zero if unknown infinint archive_size; ///< total size of the archive infinint catalog_size; ///< catalogue size if known, zero if not infinint storage_size; ///< amount of byte used to store (compressed/encrypted) data infinint data_size; ///< amount of data saved (once uncompressed/unciphered) entree_stats contents; ///< nature of saved files std::string edition; ///< archive format std::string algo_zip; ///< compression algorithm std::string user_comment; ///< user comment std::string cipher; ///< encryption algorithm std::string asym; ///< asymetrical encryption bool is_signed; ///< whether the archive is signed bool tape_marks; ///< whether the archive has tape marks (for sequential reading) std::string in_place; ///< in_place path empty string if absent }; } // end of namespace #endif dar-2.7.15/src/libdar/delta_sig_block_size.cpp0000644000175000017500000000610014636066467016203 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_LIBRSYNC_H #include #include #endif } // end extern "C" #include "integers.hpp" #include "delta_sig_block_size.hpp" #include "erreurs.hpp" #include "tools.hpp" #ifndef RS_DEFAULT_BLOCK_LEN #define RS_DEFAULT_BLOCK_LEN 2048 #endif using namespace std; namespace libdar { void delta_sig_block_size::reset() { fs_function = square2; multiplier = 1; divisor = 1; min_block_len = RS_DEFAULT_BLOCK_LEN; max_block_len = 128*1024; } bool delta_sig_block_size::operator == (const delta_sig_block_size & ref) const { return fs_function == ref.fs_function && multiplier == ref.multiplier && divisor == ref.divisor && min_block_len == ref.min_block_len && max_block_len == ref.max_block_len; } void delta_sig_block_size::check() const { if(divisor == 0) throw Erange("delta_sig_block_size::check", gettext("Invalid divisor used for delta signature block len calculation")); if(max_block_len != 0 && min_block_len > max_block_len) throw Erange("delta_sig_block_size::check", gettext("minimum size should be lesser or equal than maximum size when specifying delta signature block size formula")); } U_I delta_sig_block_size::calculate(const infinint & filesize) const { U_I ret = 0; infinint val(multiplier); switch(fs_function) { case fixed: break; case linear: val *= filesize; break; case log2: val *= tools_upper_rounded_log2(filesize); break; case square2: val *= tools_rounded_square_root(filesize); break; case square3: val *= tools_rounded_cube_root(filesize); break; default: throw SRC_BUG; } val /= divisor; // ret is already set to zero we can unstack to it val.unstack(ret); // if val is larger than the max value // that can be carried by ret, ret will // be this way set to the maximum value // it can carry if(ret < min_block_len) ret = min_block_len; if(max_block_len > 0 && ret > max_block_len) ret = max_block_len; return ret; } } // end of namespace dar-2.7.15/src/libdar/cat_blockdev.cpp0000644000175000017500000000260714636066467014474 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_blockdev.hpp" using namespace std; namespace libdar { bool cat_blockdev::operator == (const cat_entree & ref) const { const cat_blockdev *ref_blockdev = dynamic_cast(&ref); if(ref_blockdev == nullptr) return false; else return cat_device::operator == (ref); } } // end of namespace dar-2.7.15/src/libdar/cat_ignored_dir.cpp0000644000175000017500000000337114636066467015167 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_ignored_dir.hpp" using namespace std; namespace libdar { bool cat_ignored_dir::operator == (const cat_entree & ref) const { const cat_ignored_dir *ref_ignored_dir = dynamic_cast(&ref); if(ref_ignored_dir == nullptr) return false; else return cat_inode::operator == (ref); } void cat_ignored_dir::inherited_dump(const pile_descriptor & pdesc, bool small) const { cat_directory tmp = cat_directory(get_uid(), get_gid(), get_perm(), get_last_access(), get_last_modif(), get_last_change(), get_name(), 0); tmp.set_saved_status(get_saved_status()); tmp.specific_dump(pdesc, small); // dump an empty directory } } // end of namespace dar-2.7.15/src/libdar/compressor.hpp0000644000175000017500000001066414636066467014257 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file compressor.hpp /// \brief compression engine implementation /// \ingroup Private #ifndef COMPRESSOR_HPP #define COMPRESSOR_HPP #include "../my_config.h" #include "infinint.hpp" #include "integers.hpp" #include "wrapperlib.hpp" #include "proto_compressor.hpp" namespace libdar { /// \addtogroup Private /// @{ /// compression class for gzip and bzip2 algorithms class compressor : public proto_compressor { public : compressor(compression x_algo, ///< only gzip, bzip2, xz and none are supported by this class generic_file & compressed_side, ///< where to read from/write to compressed data U_I compression_level = 9 ///< compression level 1..9 ); /// \note compressed_side is not owned by the object and will remains (and must survive) /// upt to this compressor objet destruction compressor(const compressor & ref) = delete; compressor(compressor && ref) noexcept = delete; compressor & operator = (const compressor & ref) = delete; compressor & operator = (compressor && ref) noexcept = delete; virtual ~compressor(); // inherited from proto_compressor virtual compression get_algo() const override; virtual void suspend_compression() override; virtual void resume_compression() override; virtual bool is_compression_suspended() const override { return suspended; }; // inherited from generic file virtual bool skippable(skippability direction, const infinint & amount) override { return compressed->skippable(direction, amount); }; virtual bool skip(const infinint & pos) override { inherited_sync_write(); inherited_flush_read(); return compressed->skip(pos); }; virtual bool skip_to_eof() override { inherited_sync_write(); inherited_flush_read(); return compressed->skip_to_eof(); }; virtual bool skip_relative(S_I x) override { inherited_sync_write(); inherited_flush_read(); return compressed->skip_relative(x); }; virtual bool truncatable(const infinint & pos) const override { return compressed->truncatable(pos); }; virtual infinint get_position() const override { if(compr != nullptr && compr->wrap.get_total_in() != 0) throw SRC_BUG; return compressed->get_position(); }; protected : virtual void inherited_read_ahead(const infinint & amount) override { compressed->read_ahead(amount); }; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override; virtual void inherited_flush_read() override; virtual void inherited_terminate() override; private : struct xfer { wrapperlib wrap; char *buffer; U_I size; xfer(U_I sz, wrapperlib_mode mode); ~xfer(); }; xfer *compr; ///< datastructure for bzip2, gzip and zx compression (not use with compression::none bool read_mode; ///< read-only mode or write-only mode, read-write is write-only mode generic_file *compressed; ///< where to read from/write to compressed data compression algo; ///< compression algorithm used bool suspended; ///< whether compression is temporary suspended void flush_write(); ///< drop all pending write and reset compression engine }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/escape.cpp0000644000175000017500000010467214636066467013321 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "escape.hpp" #include "tools.hpp" extern "C" { #ifdef HAVE_STRIN_H #include #endif } // extern "C" using namespace std; extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif } // end extern "C" namespace libdar { //-- class static variable (well constant to be correct) // escape sequence structure // // fixed pattern (5 bytes) + escape sequence type (1 byte) = total length: 6 bytes // 0xAD 0xFD 0xEA 0x77 0x21 + { 'X' | 'H' | 'F' | 'E' | 'C' | ... } // // the fixed pattern may have its first byte (which default value is 0xAD) modified if necessary to // avoid escaping of escape sequences when using two escape objects, one writing its data to a second one. const unsigned char escape::usual_fixed_sequence[ESCAPE_SEQUENCE_LENGTH] = { ESCAPE_FIXED_SEQUENCE_NORMAL, 0xFD, 0xEA, 0x77, 0x21, 0x00 }; const infinint escape::READ_BUFFER_SIZE_INFININT = MAX_BUFFER_SIZE; //-- class routines escape::escape(generic_file *below, const set & x_unjumpable) : generic_file(below->get_mode()) { x_below = below; if(below == nullptr) throw SRC_BUG; write_buffer_size = 0; read_eof = false; already_read = 0; escape_seq_offset_in_buffer = 0; escaped_data_count_since_last_skip = 0; below_position = x_below->get_position(); unjumpable = x_unjumpable; for(U_I i = 0 ; i < ESCAPE_SEQUENCE_LENGTH; ++i) fixed_sequence[i] = usual_fixed_sequence[i]; read_buffer_size = 0; read_buffer_alloc = INITIAL_READ_BUFFER_SIZE; read_buffer = new (nothrow) char[read_buffer_alloc]; if(read_buffer == nullptr) throw Ememory("escape::escape"); } escape::~escape() { try { terminate(); } catch(...) { // ignore all exceptions } if(read_buffer != nullptr) { delete [] read_buffer; read_buffer = nullptr; } } escape & escape::operator = (const escape & ref) { generic_file *me = this; const generic_file *you = &ref; if(is_terminated()) throw SRC_BUG; *me = *you; // copying the generic_file data copy_from(ref); // copying the escape specific data return *this; } void escape::add_mark_at_current_position(sequence_type t) { // some necessary sanity checks if(is_terminated()) throw SRC_BUG; if(get_mode() == gf_read_only) throw SRC_BUG; check_below(); // sanity checks done if(t == seqt_not_a_sequence) throw Erange("escape::add_mark_at_current_position", gettext("Adding an explicit escape sequence of type seqt_not_a_sequence is forbidden")); flush_write(); escaped_data_count_since_last_skip = 0; set_fixed_sequence_for(t); x_below->write((const char*)fixed_sequence, ESCAPE_SEQUENCE_LENGTH); below_position += ESCAPE_SEQUENCE_LENGTH; } bool escape::skip_to_next_mark(sequence_type t, bool jump) { bool found = false; if(is_terminated()) throw SRC_BUG; // check whether we are not in write mode !!! if(get_mode() != gf_read_only) throw SRC_BUG; // escape implementation does not support this mode read_eof = false; // may be have been set because we reached a mark while reading data, so we now need to unset it escaped_data_count_since_last_skip = 0; do { // looking at data currently in the buffer if(escape_seq_offset_in_buffer < read_buffer_size) // amorce found in buffer { already_read = escape_seq_offset_in_buffer; // dropping data before that start of escape sequence // at that time, this may be just the start of what seems to be an escape mark, // so we need more data, which may turn out to show that this is not an escape mark if(!mini_read_buffer()) { read_eof = true; // not enough data available, thus clean_read(); } else // we could get more data to determine whether we have a mark in the buffer or not { if(escape_seq_offset_in_buffer + ESCAPE_SEQUENCE_LENGTH - 1 < read_buffer_size) { sequence_type found_type = char2type(read_buffer[escape_seq_offset_in_buffer + ESCAPE_SEQUENCE_LENGTH - 1]); if(found_type == seqt_not_a_sequence) { // this is just escaped data, so we skip it already_read = escape_seq_offset_in_buffer + ESCAPE_SEQUENCE_LENGTH; // moving the marker to the next possible escape sequence escape_seq_offset_in_buffer = already_read + trouve_amorce(read_buffer + already_read, read_buffer_size - already_read, fixed_sequence); } else // real mark found if(found_type == t) // found the expected type of mark { found = true; already_read = escape_seq_offset_in_buffer + ESCAPE_SEQUENCE_LENGTH; escape_seq_offset_in_buffer = already_read + trouve_amorce(read_buffer + already_read, read_buffer_size - already_read, fixed_sequence); } else if(jump && unjumpable.find(found_type) == unjumpable.end()) // not an unjumpable mark or jump allowed for any mark { already_read = escape_seq_offset_in_buffer + ESCAPE_SEQUENCE_LENGTH; escape_seq_offset_in_buffer = already_read + trouve_amorce(read_buffer + already_read, read_buffer_size - already_read, fixed_sequence); } else // not an unjumpable mark read_eof = true; } else // what seemed to be the start of a mark is not a mark already_read = escape_seq_offset_in_buffer; } } else // no mark in current data { // dropping all data in read_buffer, and filling it again with some new data read_buffer_size = x_below->read(read_buffer, read_buffer_alloc); below_position += read_buffer_size; if(read_buffer_size == 0) read_eof = true; already_read = 0; escape_seq_offset_in_buffer = trouve_amorce(read_buffer, read_buffer_size, fixed_sequence); } } while(!found && !read_eof); return found; } bool escape::next_to_read_is_mark(sequence_type t) { sequence_type toberead; if(is_terminated()) throw SRC_BUG; if(next_to_read_is_which_mark(toberead)) return t == toberead; else return false; } void escape::remove_unjumpable_mark(sequence_type t) { set::iterator it = unjumpable.find(t); if(is_terminated()) throw SRC_BUG; if(it != unjumpable.end()) unjumpable.erase(it); } bool escape::next_to_read_is_which_mark(sequence_type & t) { if(is_terminated()) throw SRC_BUG; check_below(); if(get_mode() != gf_read_only) throw SRC_BUG; if(escape_seq_offset_in_buffer > already_read) //no next to read mark return false; // if read_buffer size is less than ESCAPE_SEQUENCE MARK, then read some data for that if(mini_read_buffer()) { if(read_buffer_size - already_read < ESCAPE_SEQUENCE_LENGTH) throw SRC_BUG; // check the data in the read_buffer if(escape_seq_offset_in_buffer == already_read) { t = char2type(read_buffer[already_read + ESCAPE_SEQUENCE_LENGTH - 1]); if(t == seqt_not_a_sequence) throw SRC_BUG; // mini_read_buffer did not made its job properly! return true; } else return false; // no escape sequence found next to be read } else // not enough data available in x_below to form a escape sequence mark (eof reached) return false; } bool escape::skippable(skippability direction, const infinint & amount) { infinint new_amount = amount; switch(get_mode()) { case gf_read_only: return x_below->skippable(direction, new_amount); case gf_write_only: case gf_read_write: switch(direction) { case skip_backward: new_amount += ESCAPE_SEQUENCE_LENGTH; // we read some bytes before to check fo escape sequence break; case skip_forward: break; default: throw SRC_BUG; } if(direction == skip_forward) return false; else return x_below->skippable(direction, new_amount); default: throw SRC_BUG; } } bool escape::skip(const infinint & pos) { bool ret = true; if(is_terminated()) throw SRC_BUG; check_below(); escaped_data_count_since_last_skip = 0; if(get_position() == pos) return true; switch(get_mode()) { case gf_read_only: if(pos >= below_position - read_buffer_size && pos < below_position) { // requested position is in read_buffer infinint delta = below_position - pos; already_read = 0; delta.unstack(already_read); if(!delta.is_zero()) throw SRC_BUG; already_read = read_buffer_size - already_read; // this leads to the following: // alread_read = read_buffer_size - (below_position - pos); escape_seq_offset_in_buffer = already_read + trouve_amorce(read_buffer + already_read, read_buffer_size - already_read, fixed_sequence); escaped_data_count_since_last_skip = 0; read_eof = false; } else { // requested position is out of read_buffer read_eof = false; flush_or_clean(); ret = x_below->skip(pos); if(ret) below_position = pos; else below_position = x_below->get_position(); } break; case gf_write_only: if(get_position() != pos) throw Efeature("Skipping on write_only escape object"); else ret = true; break; case gf_read_write: // only backward skipping is allowed in that mode if(get_position() < pos) throw Efeature("Skipping forward not implemented in write mode for escape class"); else { char tmp_buffer[INITIAL_WRITE_BUFFER_SIZE]; infinint cur_below = below_position; U_I trouve; try { if(pos >= ESCAPE_SEQUENCE_LENGTH) { U_I lu = 0; below_position = pos - ESCAPE_SEQUENCE_LENGTH; ret = x_below->skip(below_position); if(ret) { lu = x_below->read(tmp_buffer, ESCAPE_SEQUENCE_LENGTH); below_position += lu; write_buffer_size = lu; } else below_position = x_below->get_position(); } else // skipping very close after the start of file, no escape mark can take place there { U_I width = 0; U_I lu = 0; infinint tmp = pos; tmp.unstack(width); if(tmp != 0) throw SRC_BUG; width = ESCAPE_SEQUENCE_LENGTH - width; if(!x_below->skip(0)) throw SRC_BUG; // should succeed or throw an exception in that situation (backward skipping) below_position = 0; lu = x_below->read(tmp_buffer, width); // may throw exception write_buffer_size = lu; below_position += lu; ret = true; } } catch(...) { x_below->skip(cur_below); below_position = cur_below; throw; } (void)memcpy(write_buffer, tmp_buffer, write_buffer_size); trouve = trouve_amorce(write_buffer, write_buffer_size, fixed_sequence); if(trouve == 0) // we read a whole escape sequence write_buffer_size = 0; // so we know that we can restart the lookup process here from scratch else if(trouve == write_buffer_size) // no start of escape sequence found write_buffer_size = 0; // so we know that we can restart the lookup process here from scratch else // partial escape sequence found, moving at the beginning of the write_buffer for further lookup { (void)memmove(write_buffer, write_buffer + trouve, write_buffer_size - trouve); write_buffer_size -= trouve; } } break; default: throw SRC_BUG; // this mode is not allowed } return ret; } bool escape::skip_to_eof() { bool ret; if(is_terminated()) throw SRC_BUG; check_below(); if(get_mode() != gf_read_only) throw Efeature("Skipping not implemented in write mode for escape class"); // if the buffer is neither empty not full, we cannot know what to do with this date // either place it asis in the below file, or escape it in the below file. flush_or_clean(); read_eof = true; escaped_data_count_since_last_skip = 0; ret = x_below->skip_to_eof(); below_position = x_below->get_position(); return ret; } bool escape::skip_relative(S_I x) { bool ret; if(is_terminated()) throw SRC_BUG; if(x == 0) return true; check_below(); read_eof = false; escaped_data_count_since_last_skip = 0; if(get_mode() != gf_read_only) throw Efeature("Skipping not implemented in write mode for escape class"); // if the buffer is neither empty not full, we cannot know what to do with this date // either place it asis in the below file, or escape it in the below file. flush_or_clean(); ret = x_below->skip_relative(x); if(ret) // skipping succeeded { if(x >= 0) below_position += x; else // x is negative { if(below_position < -x) below_position = 0; else below_position -= -x; // trick used, because infinint cannot be negative } } else // skipping failed, need to consult x_below to know where we are now below_position = x_below->get_position(); return ret; } infinint escape::get_position() const { if(is_terminated()) throw SRC_BUG; check_below(); if(get_mode() == gf_read_only) return below_position - read_buffer_size + already_read - escaped_data_count_since_last_skip; else return below_position + write_buffer_size - escaped_data_count_since_last_skip; } void escape::inherited_read_ahead(const infinint & amount) { if(is_terminated()) throw SRC_BUG; check_below(); if(!read_eof) { U_I avail = read_buffer_size - already_read; infinint i_avail = avail; if(i_avail < amount) x_below->read_ahead(amount - i_avail); } } U_I escape::inherited_read(char *a, U_I size) { U_I returned = 0; // ############# if EOF -> stop if(read_eof && already_read == read_buffer_size) return 0; // eof reached. (real eof or next to read is a real mark) // ############# if read_buffer not empty (we copy as needed and available data from the buffer into "a") up to the first mark bool loop = true; do { if(escape_seq_offset_in_buffer < already_read) throw SRC_BUG; U_I avail = escape_seq_offset_in_buffer - already_read; if(avail > 0) { U_I needed = size - returned; U_I min_cp = avail > needed ? needed : avail; (void)memcpy(a + returned, read_buffer + already_read, min_cp); returned += min_cp; already_read += min_cp; } if(already_read == read_buffer_size) { already_read = read_buffer_size = 0; escape_seq_offset_in_buffer = 0; } if(returned == size) return returned; if(returned > size) throw SRC_BUG; if(already_read != read_buffer_size) { // some data remains in the buffer (either more than requested, or due to a real or data mark found in it) if(already_read != escape_seq_offset_in_buffer) throw SRC_BUG; // more data was requested but could not be delivered, while no mark is next to be read from read_buffer!? if(mini_read_buffer()) // se we complete it and eventually unescape data from data mark : mini_read_buffer() does this { // there is now enough data in buffer_size to tell that this is a real completed mark if(escape_seq_offset_in_buffer == already_read) // there is a real mark next to be read in the buffer { // no more real data to be read read_eof = true; loop = false; } else { // the real mark is not the next to be read, some data have been unescaped before it loop = true; } } else // data in buffer is not a mark, just a truncated mark at an EOF so we can take it as pure data { escape_seq_offset_in_buffer = read_buffer_size; loop = true; } } else loop = false; } while(loop); // ############# OK, now, read_buffer is empty, no eof/mark met and still more data needed. // reading data from "below" directly into "a" after already placed data (if there is enough place to detect marks) loop = !read_eof; // if all data could be read, we already returned from this function, so we do not reach this statement, // if data remains in read_buffer, this is thus because we need more, but either the buffer is empty // or we met a real mark, so we reached "eof" and must not continue the reading in the following loop. while(loop) { U_I needed = size - returned; U_I read; if(needed > ESCAPE_SEQUENCE_LENGTH) { U_I delta; // filling missing data in "a" from x_below read = x_below->read(a + returned, needed); below_position += read; if(read < needed) read_eof = true; // analyse the new data, unescape data sequences, (skip left for each escaped data) and stop at the first non data escape sequence // we temporarily use the variable escape_seq_offset_in_buffer to point in "a" instead of "read_buffer" escape_seq_offset_in_buffer = remove_data_marks_and_stop_at_first_real_mark(a + returned, read, delta, fixed_sequence); escaped_data_count_since_last_skip += delta; read -= delta; if(escape_seq_offset_in_buffer > read) throw SRC_BUG; returned += escape_seq_offset_in_buffer; if(escape_seq_offset_in_buffer < read) { // mark found in data // copy back the remaining data to read_buffer if(read_buffer_alloc < read - escape_seq_offset_in_buffer) { // reallocating a larger read_buffer if(read_buffer != nullptr) { delete [] read_buffer; read_buffer = nullptr; } read_buffer_alloc = read; read_buffer = new (nothrow) char[read_buffer_alloc]; if(read_buffer == nullptr) throw Ememory("escape::inherited_read"); } read_buffer_size = read - escape_seq_offset_in_buffer; escape_seq_offset_in_buffer = 0; already_read = 0; // not setting yet read_eof, as it could be a false mark (starting like a mark, but not enough data to determin the real nature of the sequence) (void)memcpy(read_buffer, a + returned, read_buffer_size); read_eof = false; // because we moved out data from the one ready to be returned // either this was not the eof, and thus this call does not change anything // or it was EOF because data "read" was less than "needed" // but here some data will still be in read_buffer so EOF must be cleaned // be sure the mark is completed and return as much as requested data if not a complet mark needed = size - returned; if(needed > 0) { read = escape::inherited_read(a + returned, needed); // recursive call returned += read; } loop = false; } else // no mark found in data, all that got read is pure data and is directly sent to the upper layer { escape_seq_offset_in_buffer = read_buffer_size; // both should be equal to zero now loop = returned < size && !read_eof; } } else // too short space in "a" to put data and be sure there is not any mark in it, so we use read_buffer again { (void)mini_read_buffer(); // filling the read_buffer if(escape_seq_offset_in_buffer > 0) // some more data available in read_buffer returned += escape::inherited_read(a + returned, needed); // recursive call else read_eof = true; loop = false; } } // return the amount of data put into "a" return returned; } void escape::inherited_write(const char *a, U_I size) { U_I written = 0; U_I trouve; if(size == 0) return; // nothing to do try { if(write_buffer_size > 0) // some data are pending in transit { U_I initial_buffer_size = write_buffer_size; if(write_buffer_size >= ESCAPE_SEQUENCE_LENGTH - 1) throw SRC_BUG; // filling the buffer U_I delta = INITIAL_WRITE_BUFFER_SIZE - write_buffer_size; // available room in write_buffer delta = delta > size ? size : delta; (void)memcpy(write_buffer + write_buffer_size, a, delta); write_buffer_size += delta; written += delta; // checking for escape sequence in write_buffer trouve = trouve_amorce(write_buffer, write_buffer_size, fixed_sequence); if(trouve == write_buffer_size) // no escape sequence found { x_below->write(write_buffer, write_buffer_size); below_position += write_buffer_size; write_buffer_size = 0; } else // start of escape sequence found { if(trouve + ESCAPE_SEQUENCE_LENGTH - 1 <= write_buffer_size) // no doubt, we have a full escape sequence in data, we need to protect this data { x_below->write(write_buffer, trouve); below_position += trouve; set_fixed_sequence_for(seqt_not_a_sequence); x_below->write((const char *)fixed_sequence, ESCAPE_SEQUENCE_LENGTH); below_position += ESCAPE_SEQUENCE_LENGTH; // still remains valid data not yet written in write_buffer at offset 'trouve + ESCAPE_SEQUENCE_LENGTH - 1' // however this data is also in the input write_buffer (a, size) written = (trouve + ESCAPE_SEQUENCE_LENGTH - 1) - initial_buffer_size; // this way, we do not have to copy back to "a" the not yet written data ++escaped_data_count_since_last_skip; write_buffer_size = 0; // dropping all supplementary data added // it will be treated from the "a" buffer where they had been copied from } else // the escape sequence found is not complete { U_I yet_in_a = size - written; U_I missing_for_sequence = trouve + (ESCAPE_SEQUENCE_LENGTH - 1) - write_buffer_size; if(write_buffer_size < INITIAL_WRITE_BUFFER_SIZE && yet_in_a > 0) throw SRC_BUG; // write_buffer_size not filled while remains available data in "a" ! // either the escape sequence is entirely in "a" (and partially copied in write_buffer) // or there is not enough data in "a" to determin whether this start of sequence is complete or not // first, we can at least write down the data up to offset "trouve - 1" (that's "trouve" bytes). x_below->write(write_buffer, trouve); below_position += trouve; if(yet_in_a >= missing_for_sequence) // sequence entirely available with remaining data in "a" { if(trouve < initial_buffer_size) throw SRC_BUG; // some original data of write_buffer are part of the escape sequence !!! written = trouve - initial_buffer_size; write_buffer_size = 0; } else // missing data to determine the nature of the sequence { (void)memmove(write_buffer, write_buffer + trouve, write_buffer_size - trouve); write_buffer_size -= trouve; if(write_buffer_size >= ESCAPE_SEQUENCE_LENGTH - 1) throw SRC_BUG; // should never seen this if() condition if(write_buffer_size + yet_in_a > INITIAL_WRITE_BUFFER_SIZE) throw SRC_BUG; // not possible to reach normally, because yet_in_a < missing_for_sequence < SEQUENCE_LENGTH (void)memcpy(write_buffer + write_buffer_size, a+written, yet_in_a); written = size; write_buffer_size += yet_in_a; } } } } // now that we have eventually treated the write_buffer, we get two possibilities // either no escape sequence is pending in the write_buffer [write_buffer_size == 0] (escape sequence in "a" or non escape sequence found at all) // or an potential escape sequence is pending in the write_buffer, which only occurs if "a" does not contain any more // data to detemine the exact nature of this sequence [ written == size ] if(written != size && write_buffer_size > 0) throw SRC_BUG; // anormal situation, seen the previous comment. while(written < size) { U_I remains = size - written; trouve = trouve_amorce(a + written, remains, fixed_sequence); if(trouve == remains) { x_below->write(a + written, remains); below_position += remains; written = size; } else { if(trouve > 0) { x_below->write(a + written, trouve); below_position += trouve; written += trouve; } if(trouve + ESCAPE_SEQUENCE_LENGTH - 1 <= remains) // full escape sequence { set_fixed_sequence_for(seqt_not_a_sequence); x_below->write((const char *)fixed_sequence, ESCAPE_SEQUENCE_LENGTH); below_position += ESCAPE_SEQUENCE_LENGTH; written += ESCAPE_SEQUENCE_LENGTH - 1; ++escaped_data_count_since_last_skip; } else // not completed sequence { remains = size - written; if(remains >= ESCAPE_SEQUENCE_LENGTH - 1) throw SRC_BUG; // how possible is to not be able to fully determine the sequence ??? (void)memcpy(write_buffer, a + written, remains); write_buffer_size = remains; written = size; } } } } catch(Ethread_cancel & e) { below_position = x_below->get_position(); throw; } } void escape::inherited_truncate(const infinint & pos) { if(pos <= below_position) // truncating before the write_buffer { write_buffer_size = 0; // dropping content of the write_buffer escaped_data_count_since_last_skip = 0; x_below->truncate(pos); below_position = x_below->get_position(); } else if(below_position + write_buffer_size <= pos) // truncated after write_buffer { x_below->truncate(pos); if(x_below->get_position() != below_position) throw SRC_BUG; // current offset should not have changed // escaped_data_count_since_last_skip is still valid, not resetting it } else // truncating in the middle of the write_buffer { infinint i_buf_size = pos - below_position; U_I buf_size = 0; i_buf_size.unstack(buf_size); if(!i_buf_size.is_zero()) throw SRC_BUG; if(buf_size > write_buffer_size) throw SRC_BUG; // not coherent with truncating inside the write_buffer write_buffer_size = buf_size; x_below->truncate(pos); if(x_below->get_position() != below_position) throw SRC_BUG; // current offset should not have changed // escaped_data_count_since_last_skip is still valid, not resetting it } } char escape::type2char(sequence_type x) { switch(x) { case seqt_not_a_sequence: return 'X'; case seqt_file: return 'F'; case seqt_ea: return 'E'; case seqt_catalogue: return 'C'; case seqt_data_name: return 'D'; case seqt_file_crc: return 'R'; case seqt_ea_crc: return 'r'; case seqt_changed: return 'W'; case seqt_dirty: return 'I'; case seqt_failed_backup: return '!'; case seqt_fsa: return 'S'; case seqt_fsa_crc: return 's'; case seqt_delta_sig: return 'd'; case seqt_in_place: return 'P'; default: throw SRC_BUG; } } escape::sequence_type escape::char2type(char x) { switch(x) { case 'X': return seqt_not_a_sequence; case 'F': return seqt_file; case 'E': return seqt_ea; case 'C': return seqt_catalogue; case 'D': return seqt_data_name; case 'R': return seqt_file_crc; case 'r': return seqt_ea_crc; case 'W': return seqt_changed; case 'I': return seqt_dirty; case '!': return seqt_failed_backup; case 'S': return seqt_fsa; case 's': return seqt_fsa_crc; case 'd': return seqt_delta_sig; case 'P': return seqt_in_place; default: throw Erange("escape::char2type", gettext("Unknown escape sequence type")); } } void escape::clean_read() { read_buffer_size = already_read = escape_seq_offset_in_buffer = 0; read_eof = false; escaped_data_count_since_last_skip = 0; } void escape::flush_write() { check_below(); if(write_buffer_size > 0) { x_below->write(write_buffer, write_buffer_size); below_position += write_buffer_size; write_buffer_size = 0; } } void escape::copy_from(const escape & ref) { x_below = ref.x_below; write_buffer_size = ref.write_buffer_size; if(write_buffer_size > INITIAL_WRITE_BUFFER_SIZE) throw SRC_BUG; (void)memcpy(write_buffer, ref.write_buffer, write_buffer_size); read_buffer_size = ref.read_buffer_size; read_buffer_alloc = ref.read_buffer_alloc; if(read_buffer_size > read_buffer_alloc) throw SRC_BUG; if(read_buffer != nullptr) { delete [] read_buffer; read_buffer = nullptr; } read_buffer = new (nothrow) char[read_buffer_alloc]; if(read_buffer == nullptr) throw Ememory("escape::copy_from"); (void)memcpy(read_buffer, ref.read_buffer, read_buffer_size); already_read = ref.already_read; read_eof = ref.read_eof; escaped_data_count_since_last_skip = ref.escaped_data_count_since_last_skip; below_position = ref.below_position; unjumpable = ref.unjumpable; (void)memcpy(fixed_sequence, ref.fixed_sequence, ESCAPE_SEQUENCE_LENGTH); } void escape::move_from(escape && ref) noexcept { swap(x_below, ref.x_below); write_buffer_size = move(ref.write_buffer_size); swap(write_buffer, ref.write_buffer); read_buffer_size = move(ref.read_buffer_size); read_buffer_alloc = move(ref.read_buffer_alloc); swap(read_buffer, ref.read_buffer); already_read = move(ref.already_read); read_eof = move(ref.read_eof); escape_seq_offset_in_buffer = move(ref.escape_seq_offset_in_buffer); unjumpable = move(ref.unjumpable); swap(fixed_sequence, ref.fixed_sequence); escaped_data_count_since_last_skip = move(ref.escaped_data_count_since_last_skip); below_position = move(ref.below_position); } bool escape::mini_read_buffer() { U_I avail = read_buffer_size - already_read; if(avail < ESCAPE_SEQUENCE_LENGTH) { // we need more data if(already_read + ESCAPE_SEQUENCE_LENGTH >= read_buffer_alloc) { // we need room to place more data, so we skip data at the beginning of the read_buffer if(already_read < ESCAPE_SEQUENCE_LENGTH) throw SRC_BUG; // read_buffer_alloc is expected to be (much) larger than twice the escape sequence length, // so now, we never have to use memmove in place of memcpy: (void)memcpy(read_buffer, read_buffer + already_read, avail); if(escape_seq_offset_in_buffer < already_read) throw SRC_BUG; // escape_seq_offset_in_buffer, has not been updated sometime before escape_seq_offset_in_buffer -= already_read; already_read = 0; read_buffer_size = avail; } // recording up to what point data had been unescaped if(escape_seq_offset_in_buffer > read_buffer_size) throw SRC_BUG; // should not be greater than read_buffer_size else { U_I delta; // will receive the amount of byte to reduce the buffer (one byte for each data mark found) U_I delta_size; // will holds the size of the data to unescape U_I offset_in_buffer; U_I short_read; // adding some data at the end of the buffer short_read = x_below->read(read_buffer + read_buffer_size, ESCAPE_SEQUENCE_LENGTH - avail); read_buffer_size += short_read; below_position += short_read; avail = read_buffer_size - already_read; // we can continue unescaping the new data // but only what has not yet been unescaped, thus no data before escape_seq_offset_in_buffer delta_size = read_buffer_size - escape_seq_offset_in_buffer; offset_in_buffer = remove_data_marks_and_stop_at_first_real_mark(read_buffer + escape_seq_offset_in_buffer, delta_size, delta, fixed_sequence); delta_size -= delta; escaped_data_count_since_last_skip += delta; read_buffer_size = escape_seq_offset_in_buffer + delta_size; escape_seq_offset_in_buffer += offset_in_buffer; } } else // enough data, but removing any data mark found in the beginning of the read_buffer { if(escape_seq_offset_in_buffer == already_read && char2type(read_buffer[escape_seq_offset_in_buffer + ESCAPE_SEQUENCE_LENGTH - 1]) == seqt_not_a_sequence) { // next to read is a data mark, we must un-escape data U_I delta = 0; escape_seq_offset_in_buffer = already_read + remove_data_marks_and_stop_at_first_real_mark(read_buffer + already_read, read_buffer_size - already_read, delta, fixed_sequence); escaped_data_count_since_last_skip += delta; read_buffer_size -= delta; } } if(avail < ESCAPE_SEQUENCE_LENGTH) { read_eof = true; return false; } else return true; } U_I escape::trouve_amorce(const char *a, U_I size, const unsigned char escape_sequence[ESCAPE_SEQUENCE_LENGTH]) { U_I ret = 0; // points to the start of the escape sequence U_I curs = 0; // points to current byte considered U_I amorce = 0; // points to the byte to compare in the fixed sequence U_I found = ESCAPE_SEQUENCE_LENGTH - 1; // maximum number of byte to compare in fixed sequence while(curs < size && amorce < found) { if((unsigned char)a[curs] == escape_sequence[amorce]) { if(amorce == 0) ret = curs; ++amorce; } else if(amorce > 0) { curs -= amorce; amorce = 0; } ++curs; } if(curs >= size) // reached the end of the field if(amorce == 0) // and no start of escape sequence could be found ret = size; // else we return the start of the escape sequence found (or partial escape sequence if found at the end of the "a" buffer return ret; } U_I escape::remove_data_marks_and_stop_at_first_real_mark(char *a, U_I size, U_I & delta, const unsigned char escape_sequence[ESCAPE_SEQUENCE_LENGTH]) { bool loop = false; U_I ret = 0; delta = 0; do { ret += trouve_amorce(a + ret, size - ret, escape_sequence); if(ret < size) // start of escape sequence found if(ret + ESCAPE_SEQUENCE_LENGTH <= size) // we can determin the nature of the escape sequence if(char2type(a[ret + ESCAPE_SEQUENCE_LENGTH - 1]) == seqt_not_a_sequence) { (void)memmove(a + ret + ESCAPE_SEQUENCE_LENGTH - 1, a + ret + ESCAPE_SEQUENCE_LENGTH, size - ret - ESCAPE_SEQUENCE_LENGTH); ++delta; --size; // this modification is local to this method. loop = true; ret += ESCAPE_SEQUENCE_LENGTH - 1; // skipping one byte, as this is the escape data byte of which we just removed the protection } else loop = false; // real mark found else loop = false; // cannot know whether this is a real mark or just escaped data else loop = false; // no mark found } while(loop); return ret; } } // end of namespace dar-2.7.15/src/libdar/trivial_sar.cpp0000644000175000017500000003117514636066467014375 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // this was necessary to compile under Mac OS-X (boggus dirent.h) #if HAVE_STDINT_H #include #endif #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_FCNTL_H #include #endif #if STDC_HEADERS #include #endif } // end extern "C" #include "sar.hpp" #include "deci.hpp" #include "user_interaction.hpp" #include "tools.hpp" #include "erreurs.hpp" #include "cygwin_adapt.hpp" #include "deci.hpp" #include "entrepot.hpp" #include "sar_tools.hpp" #include "trivial_sar.hpp" #include "tuyau.hpp" #include "fichier_global.hpp" using namespace std; namespace libdar { trivial_sar::trivial_sar(const shared_ptr & dialog, gf_mode open_mode, const std::string & base_name, const std::string & extension, const entrepot & where, const label & internal_name, const label & data_name, const std::string & execute, bool allow_over, bool warn_over, bool force_permission, U_I permission, hash_algo x_hash, const infinint & x_min_digits, bool format_07_compatible) : generic_file(open_mode), mem_ui(dialog) { // some local variables to be used fichier_global *tmp = nullptr; const string filename = sar_tools_make_filename(base_name, 1, x_min_digits, extension); // sanity checks if(open_mode == gf_read_only) throw SRC_BUG; // initializing object fields from constructor arguments reference = nullptr; offset = 0; cur_pos = 0; end_of_slice = 0; hook = execute; base = base_name; ext = extension; of_data_name = data_name; old_sar = format_07_compatible; min_digits = x_min_digits; hook_where = where.get_full_path().display(); base_url = where.get_url(); natural_destruction = true; // creating the slice if it does not exist else failing try { try { tmp = where.open(dialog, filename, open_mode, force_permission, permission, true, //< fail if exists false, //< erase x_hash); } catch(Esystem & e) { switch(e.get_code()) { case Esystem::io_exist: if(tmp != nullptr) throw SRC_BUG; if(!allow_over) throw Erange("trivial_sar::trivial_sar", tools_printf(gettext("%S already exists, and overwritten is forbidden, aborting"), &filename)); if(warn_over) get_ui().pause(tools_printf(gettext("%S is about to be overwritten, continue ?"), &filename)); try { tmp = where.open(dialog, filename, open_mode, force_permission, permission, false, //< fail if exists true, //< erase x_hash); } catch(Esystem & e) { switch(e.get_code()) { case Esystem::io_exist: throw SRC_BUG; case Esystem::io_absent: if(tmp != nullptr) throw SRC_BUG; else { string tmp = where.get_full_path().display(); e.prepend_message(tools_printf(gettext("Directory component in %S does not exist or is a dangling symbolic link: "), &tmp)); } throw; case Esystem::io_access: case Esystem::io_ro_fs: e.prepend_message(tools_printf(gettext("Failed creating slice %S: "), &filename)); throw; // propagate the exception default: throw SRC_BUG; } } break; case Esystem::io_absent: if(tmp != nullptr) throw SRC_BUG; else { string tmp = where.get_full_path().display(); e.prepend_message(tools_printf(gettext("Directory component in %S does not exist or is a dangling symbolic link: "), &tmp)); } throw; case Esystem::io_access: case Esystem::io_ro_fs: e.prepend_message(tools_printf(gettext("Failed creating slice %S: "), &filename)); throw; // propagate the exception default: if(tmp != nullptr) throw SRC_BUG; else throw SRC_BUG; // not for the same reason, must know that reporting the same error but on a different line } } if(tmp == nullptr) throw SRC_BUG; set_info_status(CONTEXT_LAST_SLICE); reference = tmp; init(internal_name); tmp = nullptr; // setting it to null only now was necesary to be able to release the object in case of exception } catch(...) { if(tmp != nullptr) delete tmp; throw; } if(tmp != nullptr) throw SRC_BUG; } trivial_sar::trivial_sar(const shared_ptr & dialog, const std::string & pipename, bool lax) : generic_file(gf_read_only) , mem_ui(dialog) { label for_init; reference = nullptr; offset = 0; cur_pos = 0; end_of_slice = 0; hook = ""; base = ""; ext = ""; old_sar = false; min_digits = 0; hook_where = ""; base_url = ""; natural_destruction = true; set_info_status(CONTEXT_INIT); try { if(pipename == "-") reference = new (nothrow) tuyau(dialog, 0, gf_read_only); else reference = new (nothrow) tuyau(dialog, pipename, gf_read_only); if(reference == nullptr) throw Ememory("trivial_sar::trivial_sar"); for_init.clear(); init(for_init); } catch(...) { if(reference != nullptr) { delete reference; reference = nullptr; } throw; } } trivial_sar::trivial_sar(const shared_ptr & dialog, int filedescriptor, bool lax) : generic_file(gf_read_only) , mem_ui(dialog) { label for_init; reference = nullptr; offset = 0; cur_pos = 0; end_of_slice = 0; hook = ""; base = ""; ext = ""; old_sar = false; min_digits = 0; hook_where = ""; base_url = ""; natural_destruction = true; set_info_status(CONTEXT_INIT); try { reference = new (nothrow) tuyau(dialog, filedescriptor, gf_read_only); if(reference == nullptr) throw Ememory("trivial_sar::trivial_sar"); for_init.clear(); init(for_init); } catch(...) { if(reference != nullptr) { delete reference; reference = nullptr; } throw; } } trivial_sar::trivial_sar(const shared_ptr & dialog, generic_file *f, const label & internal_name, const label & data_name, bool format_07_compatible, const std::string & execute) : generic_file(gf_write_only), mem_ui(dialog) { if(f == nullptr) throw SRC_BUG; reference = f; offset = 0; cur_pos = 0; end_of_slice = 0; hook = execute; base = ""; ext = ""; of_data_name = data_name; old_sar = format_07_compatible; min_digits = 0; hook_where = ""; base_url = ""; natural_destruction = true; set_info_status(CONTEXT_LAST_SLICE); init(internal_name); } trivial_sar::~trivial_sar() { try { terminate(); } catch(...) { /// ignore all exceptions } if(reference != nullptr) delete reference; } bool trivial_sar::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; if(pos == cur_pos) return true; else cur_pos = pos; return reference->skip(pos + offset); } void trivial_sar::inherited_terminate() { if(reference != nullptr) { char last = flag_type_terminal; switch(get_mode()) { case gf_read_only: break; // explicitely accepting other value case gf_write_only: case gf_read_write: if(!old_sar) reference->write(&last, 1); // adding the trailing flag break; default: throw SRC_BUG; } reference->terminate(); delete reference; // this closes the slice so we can now eventually play with it: reference = nullptr; } if(hook != "" && natural_destruction) { switch(get_mode()) { case gf_read_only: break; case gf_write_only: case gf_read_write: tools_hook_substitute_and_execute(get_ui(), hook, hook_where, base, "1", sar_tools_make_padded_number("1", min_digits), ext, get_info_status(), base_url); break; default: throw SRC_BUG; } } } bool trivial_sar::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; if(x > 0) { bool ret = reference->skip_relative(x); if(ret) // skip succeeded cur_pos += x; else // skip failed where_am_i(); return ret; } else { U_I x_opposit = -x; if(reference->get_position() > offset + x_opposit) return reference->skip_relative(x); else return reference->skip(offset); // start of file if(cur_pos > x_opposit) { bool ret = reference->skip_relative(x); if(ret) cur_pos -= x_opposit; else where_am_i(); return ret; } else { bool ret = reference->skip(offset); cur_pos = 0; return ret; } } } void trivial_sar::init(const label & internal_name) { header tete; switch(reference->get_mode()) { case gf_read_only: tete.read(get_ui(), *reference); if(tete.get_set_flag() == flag_type_non_terminal) throw Erange("trivial_sar::trivial_sar", gettext("This archive has slices and is not possible to read from a pipe")); // if flag is flag_type_located_at_end_of_slice, we will warn at end of slice offset = reference->get_position(); of_data_name = tete.get_set_data_name(); old_sar = tete.is_old_header(); cur_pos = 0; break; case gf_write_only: case gf_read_write: tete.get_set_magic() = SAUV_MAGIC_NUMBER; tete.get_set_internal_name() = internal_name; tete.get_set_flag() = flag_type_terminal; tete.get_set_data_name() = of_data_name; if(old_sar) tete.set_format_07_compatibility(); tete.write(get_ui(), *reference); offset = reference->get_position(); cur_pos = 0; break; default: throw SRC_BUG; } } U_I trivial_sar::inherited_read(char *a, U_I size) { U_I ret = reference->read(a, size); tuyau *tmp = dynamic_cast(reference); if(tmp != nullptr && !tmp->has_next_to_read()) { if(ret > 0) { if(!old_sar) { --ret; if(a[ret] != flag_type_terminal) throw Erange("trivial_sar::inherited_read", gettext("This archive is not single sliced, more data exists in the next slices but cannot be read from the current pipe, aborting")); else end_of_slice = 1; } else end_of_slice = 1; } // else assuming EOF has already been reached } cur_pos += ret; return ret; } void trivial_sar::inherited_write(const char *a, U_I size) { cur_pos += size; try { reference->write(a, size); } catch(...) { where_am_i(); throw; } } void trivial_sar::where_am_i() { cur_pos = reference->get_position(); if(cur_pos >= offset) cur_pos -= offset; else // we are at an invalid offset (in the slice header) { if(!reference->skip(offset)) throw Edata(string("trivial_sar: ")+ gettext("Cannot skip to a valid position in file")); cur_pos = 0; } } } // end of namespace dar-2.7.15/src/libdar/compression.hpp0000644000175000017500000000560514636066467014423 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file compression.hpp /// \brief compression parameters for API /// \ingroup API #ifndef COMPRESSION_HPP #define COMPRESSION_HPP #include "../my_config.h" #include namespace libdar { /// \addtogroup API /// @{ /// the different compression algorithm available /// values to be used as argument of libdar API calls /// \note lzo1x_1_15 and lzo1x_1 should never be found in archive but instead lzo should /// be put in place. In consequence, the two letters 'j' and 'k' reserved here, shall /// well be modified to other value if necessary in the future, thus would not break /// any backward compatibility. enum class compression { none = 'n', ///< no compression gzip = 'z', ///< gzip compression (streamed) bzip2 = 'y', ///< bzip2 compression (streamed) lzo = 'l', ///< lzo compression (streamed) xz = 'x', ///< lzma compression (streamed) lzo1x_1_15 = 'j', ///< lzo degraded algo corresponding to lzop -1 lzo1x_1 = 'k', ///< lzo degraded algo corresponding to lzo -2 to lzo -6 zstd = 'd', ///< zstd compression lz4 = 'q', ///< lz4 (streamed) }; /// convert a char as stored in archive to its compression value extern compression char2compression(char a); /// return true if compression mode is per_block or false if /// this is a streamed compression extern bool char2compression_mode(char a); /// convert a compression value to a char for storing in archive extern char compression2char(compression c, bool per_block = false); /// convert a compression to its string representation extern std::string compression2string(compression c); /// convert a string representing a compression algorithm to its enum compression value extern compression string2compression(const std::string & a); // throw Erange if an unknown string is given /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_file.cpp0000644000175000017500000012500714636066467013622 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_file.hpp" #include "fichier_local.hpp" #include "pile.hpp" #include "tronc.hpp" #include "proto_compressor.hpp" #include "sparse_file.hpp" #include "null_file.hpp" #include "generic_rsync.hpp" #include "compile_time_features.hpp" #include "tools.hpp" #include "macro_tools.hpp" using namespace std; namespace libdar { cat_file::cat_file(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const string & src, const path & che, const infinint & taille, const infinint & fs_device, bool x_furtive_read_mode) : cat_inode(xuid, xgid, xperm, last_access, last_modif, last_change, src, fs_device) { chemin = (che.append(src)).display(); status = from_path; set_saved_status(saved_status::saved); offset = nullptr; size = nullptr; storage_size = nullptr; check = nullptr; dirty = false; algo_read = compression::none; // field not used for backup algo_write = compression::none; // may be set later by change_compression_algo_write() furtive_read_mode = x_furtive_read_mode; file_data_status_read = 0; file_data_status_write = 0; patch_base_check = nullptr; delta_sig = nullptr; delta_sig_read = false; read_ver = macro_tools_supported_version; try { offset = new (nothrow) infinint(0); size = new (nothrow) infinint(taille); storage_size = new (nothrow) infinint(0); if(offset == nullptr || size == nullptr || storage_size == nullptr) throw Ememory("cat_file::cat_file"); } catch(...) { if(offset != nullptr) { delete offset; offset = nullptr; } if(size != nullptr) { delete size; size = nullptr; } if(storage_size != nullptr) { delete storage_size; storage_size = nullptr; } throw; } } cat_file::cat_file(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, compression default_algo, bool small) : cat_inode(dialog, pdesc, reading_ver, saved, small) { chemin = ""; status = from_cat; size = nullptr; offset = nullptr; storage_size = nullptr; check = nullptr; dirty = false; algo_read = default_algo; // only used for archive format "03" and older algo_write = default_algo; // may be changed later using change_compression_algo_write() furtive_read_mode = false; // no used in that "status" mode file_data_status_read = 0; file_data_status_write = 0; // may be changed later using set_sparse_file_detection_write() patch_base_check = nullptr; delta_sig = nullptr; delta_sig_read = false; read_ver = reading_ver; generic_file *ptr = nullptr; pdesc->check(small); if(small) ptr = pdesc->esc; else ptr = pdesc->stack; try { size = new (nothrow) infinint(*ptr); if(size == nullptr) throw Ememory("cat_file::cat_file(generic_file)"); if(!small) // inode not partially dumped { if(saved == saved_status::saved || saved == saved_status::delta) { offset = new (nothrow) infinint(*ptr); if(offset == nullptr) throw Ememory("cat_file::cat_file(generic_file)"); if(reading_ver > 1) { storage_size = new (nothrow) infinint(*ptr); if(storage_size == nullptr) throw Ememory("cat_file::cat_file(generic_file)"); if(reading_ver > 7) { char tmp; ptr->read(&file_data_status_read, sizeof(file_data_status_read)); if((file_data_status_read & FILE_DATA_IS_DIRTY) != 0) { dirty = true; file_data_status_read &= ~FILE_DATA_IS_DIRTY; // removing the flag DIRTY } ptr->read(&tmp, sizeof(tmp)); algo_read = char2compression(tmp); algo_write = algo_read; if((file_data_status_read & FILE_DATA_HAS_DELTA_SIG) != 0) { will_have_delta_signature_structure(); if(saved == saved_status::delta && reading_ver >= archive_version(11,2)) patch_base_check = create_crc_from_file(*ptr); file_data_status_read &= ~FILE_DATA_HAS_DELTA_SIG; } file_data_status_write = file_data_status_read; } else if(storage_size->is_zero()) // in older archive storage_size was set to zero if data was not compressed { *storage_size = *size; algo_read = compression::none; algo_write = algo_read; } else { algo_read = default_algo; algo_write = algo_read; } } else // archive format version is "1" { storage_size = new (nothrow) infinint(*size); if(storage_size == nullptr) throw Ememory("cat_file::cat_file(generic_file)"); *storage_size *= 2; // compressed file should be less larger than twice // the original file // (in case the compression is very bad // and takes more place than no compression) } if(reading_ver >= 8) { check = create_crc_from_file(*ptr); if(check == nullptr) throw Ememory("cat_file::cat_file"); } // before version 8, crc was dump in any case, not only when data was saved // general case treated below } else // not saved { // since format 10 the flag is present in any case if(reading_ver >= 10) { ptr->read(&file_data_status_read, sizeof(file_data_status_read)); if((file_data_status_read & FILE_DATA_HAS_DELTA_SIG) != 0) will_have_delta_signature_structure(); file_data_status_read &= ~FILE_DATA_HAS_DELTA_SIG; } offset = new (nothrow) infinint(0); storage_size = new (nothrow) infinint(0); if(offset == nullptr || storage_size == nullptr) throw Ememory("cat_file::cat_file(generic_file)"); } // treating case of version below 8 where CRC // was saved in any case (s_saved, s_not_saved,...) if(reading_ver >= 2) { if(reading_ver < 8) { // fixed length CRC inversion from archive format "02" to "07" // present in any case, even when data is not saved // for archive version >= 8, the crc is only present // if the archive contains file data check = create_crc_from_file(*ptr, true); if(check == nullptr) throw Ememory("cat_file::cat_file"); } // archive version >= 8, crc only present if saved == s_saved/s_delta or when delta_sig (treated above) } else // no CRC in version "1" check = nullptr; if(delta_sig != nullptr) { delta_sig->read(false, reading_ver); delta_sig_read = true; } } else // partial dump has been done { if(saved == saved_status::saved || saved == saved_status::delta) { char tmp; ptr->read(&file_data_status_read, sizeof(file_data_status_read)); ptr->read(&tmp, sizeof(tmp)); algo_read = char2compression(tmp); algo_write = algo_read; if((file_data_status_read & FILE_DATA_HAS_DELTA_SIG) != 0) { will_have_delta_signature_structure(); if(saved == saved_status::delta && reading_ver >= archive_version(11,2)) patch_base_check = create_crc_from_file(*ptr); file_data_status_read &= ~FILE_DATA_HAS_DELTA_SIG; } file_data_status_write = file_data_status_read; } else // not saved { // since format 10 the flag is present in any case if(reading_ver >= 10) { ptr->read(&file_data_status_read, sizeof(file_data_status_read)); if((file_data_status_read & FILE_DATA_HAS_DELTA_SIG) != 0) { file_data_status_read &= ~FILE_DATA_HAS_DELTA_SIG; will_have_delta_signature_structure(); } file_data_status_write = file_data_status_read; } } // Now that all data has been read, setting default value for the undumped ones: offset = new (nothrow) infinint(0); // can only be set from post_constructor if(offset == nullptr) throw Ememory("cat_file::cat_file(generic_file)"); storage_size = new (nothrow) infinint(0); // cannot know the storage_size at that time if(storage_size == nullptr) throw Ememory("cat_file::cat_file(generic_file)"); check = nullptr; } } catch(...) { detruit(); throw; } } void cat_file::post_constructor(const pile_descriptor & pdesc) { cat_inode::post_constructor(pdesc); pdesc.check(true); if(offset == nullptr) throw SRC_BUG; else *offset = pdesc.esc->get_position(); // data follows right after the inode+file information+CRC } cat_file::cat_file(const cat_file & ref) : cat_inode(ref) { status = ref.status; chemin = ref.chemin; offset = nullptr; size = nullptr; storage_size = nullptr; check = nullptr; dirty = ref.dirty; algo_read = ref.algo_read; algo_write = ref.algo_write; furtive_read_mode = ref.furtive_read_mode; file_data_status_read = ref.file_data_status_read; file_data_status_write = ref.file_data_status_write; patch_base_check = nullptr; delta_sig = nullptr; delta_sig_read = ref.delta_sig_read; read_ver = ref.read_ver; try { if(ref.check != nullptr || (ref.get_escape_layer() != nullptr && (ref.get_saved_status() == saved_status::saved || ref.get_saved_status() == saved_status::delta))) { if(ref.check == nullptr) { const crc *tmp = nullptr; ref.get_crc(tmp); if(ref.check == nullptr) // failed to read the crc from escape layer throw SRC_BUG; } check = ref.check->clone(); if(check == nullptr) throw Ememory("cat_file::cat_file(cat_file)"); } else check = nullptr; offset = new (nothrow) infinint(*ref.offset); size = new (nothrow) infinint(*ref.size); storage_size = new (nothrow) infinint(*ref.storage_size); if(offset == nullptr || size == nullptr || storage_size == nullptr) throw Ememory("cat_file::cat_file(cat_file)"); if(ref.patch_base_check != nullptr) { patch_base_check = ref.patch_base_check->clone(); if(patch_base_check == nullptr) throw Ememory("cat_file::cat_file(cat_file)"); } if(ref.delta_sig != nullptr) { delta_sig = new (nothrow) cat_delta_signature(*ref.delta_sig); if(delta_sig == nullptr) throw Ememory("cat_file::cat_file(cat_file)"); } } catch(...) { detruit(); throw; } } void cat_file::inherited_dump(const pile_descriptor & pdesc, bool small) const { generic_file *ptr = nullptr; char flags = has_delta_signature_structure() ? FILE_DATA_HAS_DELTA_SIG : 0; // setting ptr pdesc.check(small); if(small) ptr = pdesc.esc; else ptr = pdesc.stack; // setting flag flags |= file_data_status_write; if(dirty) flags |= FILE_DATA_IS_DIRTY; // dumping the cat_inode part of the object cat_inode::inherited_dump(pdesc, small); // dumping th cat_file part of the inode size->dump(*ptr); if(!small) { if(get_saved_status() == saved_status::saved || get_saved_status() == saved_status::delta) { char tmp = compression2char(algo_write); offset->dump(*ptr); storage_size->dump(*ptr); ptr->write(&flags, sizeof(flags)); ptr->write(&tmp, sizeof(tmp)); if(get_saved_status() == saved_status::delta) { if(patch_base_check == nullptr) throw SRC_BUG; patch_base_check->dump(*ptr); } } else // since format 9 flag is present in any case to know whether object has delta signature ptr->write(&flags, sizeof(flags)); if(get_saved_status() == saved_status::saved || get_saved_status() == saved_status::delta) { // since archive version 8, crc is only present for saved inode if(check == nullptr) throw SRC_BUG; // no CRC to dump! else check->dump(*ptr); } if(has_delta_signature_structure()) delta_sig->dump_metadata(*ptr); } else // we only know whether the file will be compressed or using sparse_file data structure { if(get_saved_status() == saved_status::saved || get_saved_status() == saved_status::delta) { char tmp = compression2char(algo_write); (void)ptr->write(&flags, sizeof(flags)); (void)ptr->write(&tmp, sizeof(tmp)); if(get_saved_status() == saved_status::delta) { if(!has_patch_base_crc()) throw SRC_BUG; patch_base_check->dump(*ptr); } } else { // since format 9 the flag is present in any case (void)ptr->write(&flags, sizeof(flags)); } } } bool cat_file::has_changed_since(const cat_inode & ref, const infinint & hourshift, comparison_fields what_to_check) const { const cat_file *tmp = dynamic_cast(&ref); if(tmp != nullptr) return cat_inode::has_changed_since(*tmp, hourshift, what_to_check) || *size != *(tmp->size); else throw SRC_BUG; } generic_file *cat_file::get_data(get_data_mode mode, shared_ptr delta_sig_mem, U_I signature_block_size, shared_ptr delta_ref, const crc **checksum) const { generic_file *ret = nullptr; try { // sanity checks if(!can_get_data()) throw Erange("cat_file::get_data", gettext("cannot provide data from a \"not saved\" file object")); if(delta_ref && get_saved_status() != saved_status::delta) throw SRC_BUG; if(delta_ref && status != from_path) throw Efeature("building an binary difference (rsync) for a file located in an archive"); if(status == empty) throw Erange("cat_file::get_data", gettext("data has been cleaned, object is now empty")); if(delta_sig_mem) { if(delta_sig_mem->get_mode() == gf_read_only) throw SRC_BUG; else delta_sig_mem->reset(); switch(mode) { case keep_compressed: throw SRC_BUG; case keep_hole: throw SRC_BUG; case normal: if(get_sparse_file_detection_read()) throw SRC_BUG; // sparse_file detection is not compatible with delta signature calculation // for merging operation, this is the object where we write to that can perform hole detection // while the object from we read is able to perform delta signature calculation break; case plain: break; default: throw SRC_BUG; } } if(delta_ref) { switch(mode) { case keep_compressed: throw SRC_BUG; case keep_hole: throw SRC_BUG; case normal: if(get_sparse_file_detection_read()) throw SRC_BUG; break; case plain: break; default: throw SRC_BUG; } } // if(status == from_path) { fichier_local *tmp = nullptr; if(mode != normal && mode != plain) throw SRC_BUG; // keep compressed/keep_hole is not possible on an inode take from a filesystem ret = tmp = new (nothrow) fichier_local(chemin, furtive_read_mode); try { if(tmp != nullptr) // telling *tmp to flush the data from the cache as soon as possible tmp->fadvise(fichier_global::advise_dontneed); } catch(Erange & e) { // silently ignoring fadvise related error, // this is not crucial } if(delta_sig_mem || delta_ref) { pile *data = new (nothrow) pile(); if(data == nullptr) throw Ememory("cat_file::get_data"); try { data->push(tmp); } catch(...) { delete data; throw; } ret = data; if(delta_sig_mem) { generic_rsync *delta = new (nothrow) generic_rsync(delta_sig_mem.get(), signature_block_size, data->top()); if(delta == nullptr) throw Ememory("cat_file::get_data"); try { data->push(delta); } catch(...) { delete delta; throw; } } if(delta_ref) { generic_rsync *diff = new (nothrow) generic_rsync(delta_ref.get(), data->top(), tools_file_size_to_crc_size(get_size()), checksum); if(diff == nullptr) throw Ememory("cat_file::get_data"); try { data->push(diff); } catch(...) { delete diff; throw; } } } } else // inode from archive if(get_pile() == nullptr) throw SRC_BUG; // set_archive_localisation never called or with a bad argument else if(get_pile()->get_mode() == gf_write_only) throw SRC_BUG; // cannot get data from a write-only file !!! else { // we will return a small stack of generic_file over the catalogue stack pile *data = new (nothrow) pile(); if(data == nullptr) throw Ememory("cat_file::get_data"); try { // changing the compression algo of the archive stack if(get_compression_algo_read() != compression::none && mode != keep_compressed) { if(get_compression_algo_read() != get_compressor_layer()->get_algo()) { get_pile()->flush_read_above(get_compressor_layer()); get_compressor_layer()->resume_compression(); if(get_compression_algo_read() != get_compressor_layer()->get_algo()) throw SRC_BUG; } // else nothing to do, compressor is already properly configured } else // disabling de-compression { if(get_compressor_layer()->get_algo() != compression::none) { get_pile()->flush_read_above(get_compressor_layer()); get_compressor_layer()->suspend_compression(); } // else nothing to do, de-compression is already disabled } // adding a tronc object in the local stack object when no compression is used if(!get_small_read()) { if(get_compression_algo_read() == compression::none) { generic_file *tmp = new (nothrow) tronc(get_pile(), *offset, *storage_size, gf_read_only); if(tmp == nullptr) throw Ememory("cat_file::get_data"); try { data->push(tmp); } catch(...) { delete tmp; throw; } data->skip(0); } else get_pile()->skip(*offset); } // determining on which layer to rely on for the next to come sparse file generic_file *parent = data->is_empty() ? get_pile() : data->top(); // adding a sparse_file object in top of the local stack // // if a sparse_file object is to be used, it must be placed on top of the // returned stack, in order to benefit from the sparse_file::copy_to() specific methods // that can restore holes if(get_sparse_file_detection_read() && mode != keep_compressed && mode != keep_hole // fixing bug in dar up to and including 2.6.7 // where the sparse flag was not removed when // doing a delta patch, while no sparse file // detection was done to store the delta batch // into the archive as expected. the delta patch // has very little chance to produce holes in // the resulting data. Version greater than 2.6.7 // will remove the sparse flag when performing delta patch // which will avoid this extra code && get_saved_status() != saved_status::delta) { sparse_file *stmp = new (nothrow) sparse_file(parent); if(stmp == nullptr) throw Ememory("cat_file::get_data"); try { data->push(stmp); } catch(...) { delete stmp; throw; } switch(mode) { case keep_compressed: case keep_hole: throw SRC_BUG; case normal: break; case plain: stmp->copy_to_without_skip(true); break; default: throw SRC_BUG; } } // normally having both delta_sig and sparse_file is excluded by sanity checks // so we do not add delta_sig above sparse_file which would deserve sparse file // detection (archive reading, restoration, testing, diffing, ...), // but while merging we may have both sparse_file to provide plain file without hole // and generic_rsync to compute the delta signature of the plain file parent = data->is_empty() ? get_pile() : data->top(); if(delta_sig_mem) { generic_rsync *delta = new (nothrow) generic_rsync(delta_sig_mem.get(), signature_block_size, parent); if(delta == nullptr) throw Ememory("cat_file::get_data"); try { data->push(delta); } catch(...) { delete delta; throw; } } // if the stack to return is empty adding a tronc // to have the proper offset zero at the beginning of the data // // but we must not check the offset coherence between current read // position and underlying position when compression is used below // because it would lead the tronc to ask the compressor to seek // in the compressed data at the current position of uncompressed data if(data->is_empty()) { tronc *tronc_tmp; generic_file *tmp = tronc_tmp = new (nothrow) tronc(get_pile(), *offset, gf_read_only); if(tmp == nullptr) throw Ememory("cat_file::get_data"); if(tronc_tmp == nullptr) throw SRC_BUG; try { tronc_tmp->check_underlying_position_while_reading_or_writing(false); data->push(tmp); } catch(...) { delete tmp; throw; } } ret = data; } catch(...) { delete data; throw; } } } catch(...) { if(ret != nullptr) delete ret; throw; } if(ret == nullptr) throw Ememory("cat_file::get_data"); else return ret; } void cat_file::clean_data() { switch(status) { case from_path: chemin = ""; // smallest possible memory allocation break; case from_cat: *offset = 0; // smallest possible memory allocation // warning, cannot change "size", as it is dump() in catalogue later break; case empty: // nothing to do break; default: throw SRC_BUG; } status = empty; } void cat_file::set_offset(const infinint & r) { if(status == empty) throw SRC_BUG; *offset = r; } const infinint & cat_file::get_offset() const { if(get_saved_status() != saved_status::saved && get_saved_status() != saved_status::delta) throw SRC_BUG; if(offset == nullptr) throw SRC_BUG; return *offset; } void cat_file::set_crc(const crc &c) { if(check != nullptr) { delete check; check = nullptr; } check = c.clone(); if(check == nullptr) throw Ememory("cat_file::set_crc"); } bool cat_file::get_crc(const crc * & c) const { if(get_escape_layer() == nullptr) if(check != nullptr) { c = check; return true; } else return false; else { if(get_saved_status() == saved_status::saved || get_saved_status() == saved_status::delta) { if(check == nullptr) { try { get_pile()->flush_read_above(get_escape_layer()); if(get_escape_layer()->skip_to_next_mark(escape::seqt_file_crc, false)) { crc *tmp = nullptr; // first, recording storage_size (needed when isolating a catalogue in sequential read mode) if(storage_size->is_zero()) { infinint pos = get_escape_layer()->get_position(); if(pos < *offset) throw SRC_BUG; else *storage_size = pos - *offset; } else throw SRC_BUG; // how is this possible ??? it should always be zero in sequential read mode ! tmp = create_crc_from_file(*(get_escape_layer())); if(tmp == nullptr) throw SRC_BUG; else { const_cast(this)->check = tmp; tmp = nullptr; // object now owned by "this" } } else throw Erange("cat_file::cat_file", gettext("can't read data CRC: No escape mark found for that file")); } catch(...) { // we assign a default crc to the object // to avoid trying reading it again later on if(check == nullptr) { const_cast(this)->check = new (nothrow) crc_n(1); if(check == nullptr) throw Ememory("cat_file::cat_file"); } throw; } } if(check == nullptr) throw SRC_BUG; // should not be nullptr now! else c = check; return true; } else return false; } } bool cat_file::get_crc_size(infinint & val) const { if(check != nullptr) { val = check->get_size(); return true; } else return false; } bool cat_file::has_patch_base_crc() const { if(patch_base_check == nullptr && delta_sig != nullptr && delta_sig->has_patch_base_crc()) { const crc *tmp = nullptr; if(!delta_sig->get_patch_base_crc(tmp)) throw SRC_BUG; // was reported to have such field just above if(tmp == nullptr) throw SRC_BUG; const_cast(this)->patch_base_check = tmp->clone(); if(patch_base_check == nullptr) throw Ememory("cat_file::cat_file"); } return patch_base_check != nullptr; } bool cat_file::get_patch_base_crc(const crc * & c) const { if(patch_base_check != nullptr) { c = patch_base_check; return true; } else return false; } void cat_file::set_patch_base_crc(const crc & c) { if(delta_sig == nullptr) throw SRC_BUG; clean_patch_base_crc(); patch_base_check = c.clone(); if(patch_base_check == nullptr) throw Ememory("cat_file::set_patch_base_crc"); } bool cat_file::has_patch_result_crc() const { if(delta_sig != nullptr && delta_sig->is_pending_read()) { // if read() has not yet been called (pending_read() returned true) // at the time one asks whether a patch_result is // available, it means the object is not // yet fully read while already used, // which is only the case in sequential-read // where from the "true" value as first argument // of delta_sig->read() below escape* esc = get_escape_layer(); if(esc != nullptr) { get_pile()->flush_read_above(esc); if(esc->skip_to_next_mark(escape::seqt_delta_sig, false)) delta_sig->read(true, read_ver); else return false; // delta_sig was created, thus we may have hit // either a bug or a data corruption, letting the caller deciding } else throw SRC_BUG; // we should not be in read_pending() and without escape layer } return delta_sig != nullptr && delta_sig->has_patch_result_crc(); } bool cat_file::get_patch_result_crc(const crc * & c) const { if(delta_sig != nullptr) { if(delta_sig->has_patch_result_crc()) { delta_sig->get_patch_result_crc(c); return true; } else throw SRC_BUG; } else if(check != nullptr && get_saved_status() == saved_status::saved) { c = check; return true; } else return false; } void cat_file::set_patch_result_crc(const crc & c) { if(!has_delta_signature_structure()) throw SRC_BUG; // the patch_result_crc is only // here to record the real CRC a file has once // restored. When a file is a delta patch, the "check" // field is only the checksum of the patch, not of // the resulting patched file. // If we want to do a new delta patch on a existing patch // we will need the real CRC of the data not the CRC // of the current patch at restoration time to check // this second patch is applied to the correct file. // // This field is thus used only when delta_signature // is present and when the file's data is already a // patch (saved_status::delta) or the saved_status::not_saved status of a // unchanged patch. delta_sig->set_patch_result_crc(c); } void cat_file::will_have_delta_signature_structure() { generic_file *ptr = nullptr; proto_compressor *zip = nullptr; if(delta_sig == nullptr) { switch(status) { case empty: throw SRC_BUG; case from_path: delta_sig = new (nothrow) cat_delta_signature(); break; case from_cat: ptr = get_read_cat_layer(get_small_read()); if(ptr == nullptr) throw SRC_BUG; zip = get_compressor_layer(); if(zip == nullptr) throw SRC_BUG; delta_sig = new (nothrow) cat_delta_signature(ptr, zip); break; default: throw SRC_BUG; } if(delta_sig == nullptr) throw Ememory("cat_file::will_have_delta_signature()"); } } void cat_file::will_have_delta_signature_available() { will_have_delta_signature_structure(); if(delta_sig == nullptr) throw SRC_BUG; delta_sig->will_have_signature(); } void cat_file::dump_delta_signature(shared_ptr & sig, U_I sig_block_size, generic_file & where, bool small) const { infinint crc_size; if(delta_sig == nullptr) throw SRC_BUG; const_cast(delta_sig)->set_sig(sig, sig_block_size); delta_sig->dump_data(where, small, read_ver); } void cat_file::dump_delta_signature(generic_file & where, bool small) const { infinint crc_size; if(delta_sig == nullptr) throw SRC_BUG; const_cast(delta_sig)->set_sig(); delta_sig->dump_data(where, small, read_ver); } void cat_file::read_delta_signature_metadata() const { proto_compressor *from = nullptr; escape *esc = nullptr; bool small = get_small_read(); if(delta_sig == nullptr) throw SRC_BUG; if(!delta_sig_read) { try { switch(status) { case empty: throw SRC_BUG; case from_path: throw SRC_BUG; // signature is calculated while reading the data (get_data()) and kept by the caller case from_cat: from = get_compressor_layer(); if(from == nullptr) throw SRC_BUG; else from->suspend_compression(); esc = get_escape_layer(); if(small && esc == nullptr) throw SRC_BUG; break; default: throw SRC_BUG; } if(small) { if(!esc->skip_to_next_mark(escape::seqt_delta_sig, true)) throw Erange("cat_file::read_delta_signature", gettext("can't find mark for delta signature")); } delta_sig->read(small, read_ver); if(read_ver < archive_version(11,2)) { const crc *tmp; if(delta_sig->get_patch_base_crc(tmp)) const_cast(this)->set_patch_base_crc(*tmp); else const_cast(this)->clean_patch_base_crc(); } delta_sig_read = true; } catch(Egeneric & e) { if(delta_sig != nullptr) { cat_file *me = const_cast(this); if(me == nullptr) throw SRC_BUG; delete delta_sig; me->delta_sig = nullptr; } e.prepend_message(gettext("Error while retrieving delta signature from the archive: ")); throw; } } } void cat_file::read_delta_signature(shared_ptr & delta_sig_ret, U_I & block_len) const { read_delta_signature_metadata(); if(delta_sig->can_obtain_sig()) delta_sig_ret = delta_sig->obtain_sig(read_ver); else delta_sig_ret.reset(); block_len = delta_sig->obtain_sig_block_size(); } void cat_file::drop_delta_signature_data() const { if(delta_sig != nullptr) { cat_file *me = const_cast(this); if(me == nullptr) throw SRC_BUG; me->delta_sig->drop_sig(); } } bool cat_file::has_same_delta_signature(const cat_file & ref) const { shared_ptr sig_me; shared_ptr sig_you; U_I my_sig_block_len; U_I your_sig_block_len; read_delta_signature(sig_me, my_sig_block_len); ref.read_delta_signature(sig_you, your_sig_block_len); if(!sig_me) throw SRC_BUG; if(!sig_you) throw SRC_BUG; if(my_sig_block_len != your_sig_block_len) return false; if(sig_me->size() != sig_you->size()) return false; else return *sig_me == *sig_you; } void cat_file::clear_delta_signature_only() { if(delta_sig != nullptr) { if(get_saved_status() == saved_status::delta) delta_sig->drop_sig(); else clear_delta_signature_structure(); } } void cat_file::clear_delta_signature_structure() { if(delta_sig != nullptr) { delete delta_sig; delta_sig = nullptr; } clean_patch_base_crc(); } bool cat_file::same_data_as(const cat_file & other, bool check_data, const infinint & hourshift) { bool ret = true; try { sub_compare_internal(other, false, check_data, hourshift); } catch(Erange & e) { ret = false; } return ret; } void cat_file::sub_compare(const cat_inode & other, bool isolated_mode) const { sub_compare_internal(other, !isolated_mode, true, 0); } void cat_file::sub_compare_internal(const cat_inode & other, bool can_read_my_data, bool can_read_other_data, const infinint & hourshift) const { const cat_file *f_other = dynamic_cast(&other); if(f_other == nullptr) throw SRC_BUG; // cat_inode::compare should have called us with a correct argument if(get_size() != f_other->get_size()) { infinint s1 = get_size(); infinint s2 = f_other->get_size(); throw Erange("cat_file::sub_compare", tools_printf(gettext("not same size: %i <--> %i"), &s1, &s2)); } if(!tools_is_equal_with_hourshift(hourshift, get_last_modif(), other.get_last_modif())) { string s1 = tools_display_date(get_last_modif()); string s2 = tools_display_date(other.get_last_modif()); throw Erange("cat_file::sub_compare_internal", tools_printf(gettext("difference of last modification date: %S <--> %S"), &s1, &s2)); } if(!can_read_other_data) // we cannot compare data, nor CRC, no signature, so we just rely on mtime { // not that hourshift (field from cat_inode) is only used in that context // the mtime comparison is also done at cat_inode level when calling compare() // but in that contaxt can_read_other_data is true, this we do not compare twice mtimes return; //<<< we stop the method here in that case } if(f_other->get_saved_status() != saved_status::saved) throw SRC_BUG; // we should compare with a plain object provided by a filesystem object if(get_saved_status() == saved_status::saved && can_read_my_data) { // compare file content and CRC generic_file *me = get_data(normal, nullptr, 0, nullptr); if(me == nullptr) throw SRC_BUG; try { generic_file *you = f_other->get_data(normal, nullptr, 0, nullptr); if(you == nullptr) throw SRC_BUG; // requesting read_ahead for the peer object // if the object is found on filesystem its // storage_size is zero, which lead a endless // read_ahead request, suitable for the current // context: try { crc *value = nullptr; const crc *original = nullptr; infinint crc_size; if(has_crc()) { if(get_crc(original)) { if(original == nullptr) throw SRC_BUG; crc_size = original->get_size(); } else throw SRC_BUG; // has a crc but cannot get it?!? } else // we must not fetch the crc yet, especially when perfoming a sequential read crc_size = tools_file_size_to_crc_size(f_other->get_size()); try { infinint err_offset; if(me->diff(*you, get_storage_size(), f_other->get_storage_size(), crc_size, value, err_offset)) throw Erange("cat_file::sub_compare", tools_printf(gettext("different file data, offset of first difference is: %i"), &err_offset)); // data is the same, comparing the CRC values if(get_crc(original)) { if(value == nullptr) throw SRC_BUG; if(original->get_size() != value->get_size()) throw Erange("cat_file::sub_compare", gettext("Same data but CRC value could not be verified because we did not guessed properly its width (sequential read restriction)")); if(*original != *value) throw Erange("cat_file::sub_compare", gettext("Same data but stored CRC does not match the data!?!")); } // else old archive without CRC } catch(...) { if(value != nullptr) delete value; throw; } if(value != nullptr) delete value; } catch(...) { delete you; throw; } delete you; } catch(...) { delete me; throw; } delete me; } else if(has_delta_signature_available() && (compile_time::librsync() || f_other->has_delta_signature_available())) { // calculate delta signature of file and comparing them if(f_other->has_delta_signature_available()) // this should never be the case, but well it does not hurt taking this case into account { // both only have delta signature if(!has_same_delta_signature(*f_other)) throw Erange("cat_file::sub_compare", gettext("Delta signature do not match")); } else { // we only have signature and you only have data shared_ptr sig_me; shared_ptr sig_you(new (nothrow) memory_file()); null_file trash = gf_write_only; generic_file *data; U_I block_len; if(!sig_you) throw Ememory("cat_file::sub_compare_internal"); read_delta_signature(sig_me, block_len); if(!sig_me) throw SRC_BUG; data = f_other->get_data(normal, sig_you, block_len, shared_ptr()); if(data == nullptr) throw SRC_BUG; try { data->copy_to(trash); } catch(...) { delete data; throw; } delete data; data = nullptr; try { infinint size_me = sig_me->size(); infinint size_you = sig_you->size(); if(size_me != size_you) throw Erange("cat_file::sub_compare", tools_printf(gettext("Delta signature do not have the same size: %i <--> %i"), &size_me, &size_you)); if(*sig_me != *sig_you) // comparing file's content throw Erange("cat_file::sub_compare", gettext("Delta signature have the same size but do not match")); } catch(...) { drop_delta_signature_data(); throw; } drop_delta_signature_data(); } } else // isolated_mode and no signature or no data { const crc *my_crc = nullptr; if(get_saved_status() == saved_status::delta && !has_patch_result_crc()) throw SRC_BUG; // if we have a CRC available for data if((get_saved_status() != saved_status::delta && get_crc(my_crc)) || (get_saved_status() == saved_status::delta && get_patch_result_crc(my_crc))) { // just compare CRC (as for isolated_mode) if(my_crc == nullptr) throw SRC_BUG; generic_file *you = f_other->get_data(normal, nullptr, 0, nullptr); if(you == nullptr) throw SRC_BUG; try { crc *other_crc = nullptr; try { null_file ignore = gf_write_only; you->copy_to(ignore, my_crc->get_size(), other_crc); if(my_crc->get_size() != other_crc->get_size() || *my_crc != *other_crc) throw Erange("cat_file::compare", tools_printf(gettext("CRC difference concerning file's data"))); } catch(...) { if(other_crc != nullptr) delete other_crc; throw; } if(other_crc != nullptr) delete other_crc; } catch(...) { delete you; throw; } delete you; } } } void cat_file::clean_patch_base_crc() { if(patch_base_check != nullptr) { delete patch_base_check; patch_base_check = nullptr; } } void cat_file::detruit() { if(offset != nullptr) { delete offset; offset = nullptr; } if(size != nullptr) { delete size; size = nullptr; } if(storage_size != nullptr) { delete storage_size; storage_size = nullptr; } if(check != nullptr) { delete check; check = nullptr; } if(delta_sig != nullptr) { delete delta_sig; delta_sig = nullptr; } clean_patch_base_crc(); } } // end of namespace dar-2.7.15/src/libdar/crypto_sym.hpp0000644000175000017500000001721014636066467014265 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file crypto_sym.hpp /// \brief class crypto_sym for symetrical cipherings /// \ingroup Private #ifndef CRYPTO_SYM_HPP #define CRYPTO_SYM_HPP extern "C" { #if HAVE_GCRYPT_H #ifndef GCRYPT_NO_DEPRECATED #define GCRYPT_NO_DEPRECATED #endif #include #endif } #include "../my_config.h" #include #include "crypto_module.hpp" #include "secu_string.hpp" #include "crypto.hpp" #include "archive_aux.hpp" #include "archive_version.hpp" namespace libdar { /// \addtogroup Private /// @{ inline bool crypto_min_ver_libgcrypt_no_bug() { #if CRYPTO_AVAILABLE return gcry_check_version(MIN_VERSION_GCRYPT_HASH_BUG); #else return true; #endif } /// symetrical strong encryption, interface to grypt library class crypto_sym : public crypto_module { public: crypto_sym(const secu_string & password, ///< cipher password const archive_version & reading_ver, ///< archive format version crypto_algo algo, ///< ciphering algorithm to use const std::string & salt, ///< if set empty, the salt is generated by the crypto_sym when use_pkcs5 is set to true, else it is not used const infinint & iteration_count, ///< not used if use_pkcs5 is not set hash_algo kdf_hash, ///< not used if use_pkcs5 is not set bool use_pkcs5 ///< must be set to true when password is human defined to add a key derivation ); crypto_sym(const crypto_sym & ref) { copy_from(ref); }; crypto_sym(crypto_sym && ref) noexcept { try { move_from(std::move(ref)); } catch(...) {} }; crypto_sym & operator = (const crypto_sym & ref) { detruit(); copy_from(ref); return *this; }; crypto_sym & operator = (crypto_sym && ref) noexcept { try { detruit(); move_from(std::move(ref)); } catch(...) {} return *this; }; virtual ~crypto_sym() noexcept { try { detruit(); } catch(...) {} }; // inherited from class crypto_module virtual U_32 encrypted_block_size_for(U_32 clear_block_size) override; virtual U_32 clear_block_allocated_size_for(U_32 clear_block_size) override; virtual U_32 encrypt_data(const infinint & block_num, const char *clear_buf, const U_32 clear_size, const U_32 clear_allocated, char *crypt_buf, U_32 crypt_size) override; virtual U_32 decrypt_data(const infinint & block_num, const char *crypt_buf, const U_32 crypt_size, char *clear_buf, U_32 clear_size) override; virtual std::unique_ptr clone() const override; /// give access to the calculated or provided salt const std::string & get_salt() const { return sel; }; /// returns the max key length in octets for the given algorithm static size_t max_key_len(crypto_algo algo); /// returns the max key length in octets to use to compute a key from a user provided password static size_t max_key_len_libdar(crypto_algo algo); /// check whether the given password is reported as strong in regard to the given cipher static bool is_a_strong_password(crypto_algo algo, const secu_string & password); private: std::string sel; ///< the salt #if CRYPTO_AVAILABLE archive_version reading_ver; ///< the currently followed archive format crypto_algo algo; ///< algo ID in libgcrypt secu_string hashed_password; ///< pkcs5 hashed password or provided password if pkcs5 is not needed secu_string essiv_password; ///< password for essiv gcry_cipher_hd_t main_clef; ///< used to encrypt/decrypt the data gcry_cipher_hd_t essiv_clef; ///< used to build the Initialization Vector size_t algo_block_size; ///< the block size of the algorithm (main key) unsigned char *ivec; ///< algo_block_size allocated in secure memory to be used as Initial Vector for main_clef void init_hashed_password(const secu_string & password, bool use_pkcs5, const std::string & salt, infinint iteration_count, hash_algo kdf_hash, crypto_algo algo); void init_essiv_password(const secu_string & key, unsigned int IV_hashing); void init_main_clef(const secu_string & password, ///< key password crypto_algo algo ///< only use when use_pkcs5 ); void init_essiv_clef(const secu_string & essiv_password, U_I IV_cipher, U_I main_cipher_algo_block_size); void init_algo_block_size(crypto_algo algo); void init_ivec(crypto_algo algo, size_t algo_block_size); void detruit(); void copy_from(const crypto_sym & ref); void move_from(crypto_sym && ref); /// creates a blowfish key using as key a SHA1 of the given string (no IV assigned) /// \note such key is intended to be used to generate IV for the main key static void get_IV_cipher_and_hashing(const archive_version & ver, U_I main_cipher, U_I & cipher, U_I & hashing); /// Fills up a new initial vector based on a reference and and a encryption key /// \param[in] ref is the reference to base the IV on /// \param[in] ivec is the address where to drop down the new IV /// \param[in] size is the amount of data allocated at ivec address /// \param[in] IVkey is the key used to generate the IV data /// \note the IV is created by encrypting a hash of ref with IVkey static void make_ivec(const infinint & ref, unsigned char *ivec, U_I size, const gcry_cipher_hd_t & IVkey); /// Create a hash string of requested length from a given password and interation cout static secu_string pkcs5_pass2key(const secu_string & password, ///< human provided password const std::string & salt, ///< salt string U_I iteration_count, ///< number of time to shake the melange U_I hash_gcrypt, ///< hashing fonction used for key derivation (SHA1 historically) U_I output_length); ///< length of the string to return /// create a hash using argon2 key derivation algorithm static secu_string argon2_pass2key(const secu_string & password, const std::string & salt, U_I iteration_count, U_I output_length); /// converts libdar crypto algo designation to index used by libgcrypt static U_I get_algo_id(crypto_algo algo); /// generates a random salt of given size static std::string generate_salt(U_I size); #ifdef LIBDAR_NO_OPTIMIZATION static bool self_tested; static void self_test(void); #endif #else void detruit() { throw Ecompilation(gettext("Strong encryption support (libgcrypt)")); }; void copy_from(const crypto_sym & ref) { throw Ecompilation(gettext("Strong encryption support (libgcrypt)")); }; void move_from(crypto_sym && ref) { throw Ecompilation(gettext("Strong encryption support (libgcrypt)")); }; #endif }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/crit_action.hpp0000644000175000017500000002147314636066467014361 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file crit_action.hpp /// \brief contains classes that let the user define the policy for overwriting files /// \ingroup API #ifndef CRIT_ACTION_HPP #define CRIT_ACTION_HPP #include "../my_config.h" #include #include "criterium.hpp" namespace libdar { /// \addtogroup API /// @{ /// no need to dig into class cat_nomme here class cat_nomme; /// the possible actions for overwriting data enum over_action_data { data_preserve, ///< do not overwrite (keep the 'in place' entry) data_overwrite, ///< overwirte the 'in place' entry by the 'to be added' one data_preserve_mark_already_saved, ///< keep the 'in place' but mark it as already saved in the archive of reference data_overwrite_mark_already_saved, ///< overwrite the 'in place' but mark the 'to be added' as already saved in the archive of reference data_remove, ///< remove the original data/EA (file is completely deleted) data_undefined, ///< action still undefined at this step of the evaluation data_ask ///< ask for user decision about file's data }; /// the possible action for overwriting EA /// define the action to apply to each EA entry (not to the EA set of a particular inode) enum over_action_ea { EA_preserve, ///< keep the EA of the 'in place' entry EA_overwrite, ///< keep the EA of the 'to be added' entry EA_clear, ///< drop the EA for the elected entry EA_preserve_mark_already_saved, ///< drop any EA but mark them as already saved in the archive of reference (ctime is the one of the 'in place' inode) EA_overwrite_mark_already_saved, ///< drop any EA but mark them as already saved in the archive of reference (ctime is the one of the 'to be added' inode) EA_merge_preserve, ///< merge EA but do not overwrite existing EA of 'in place' by one of the same name of 'to be added' inode EA_merge_overwrite, ///< merge EA but if both inode share an EA with the same name, take keep the one of the 'to be added' inode EA_undefined, ///< action still undefined at this step of the evaluation EA_ask ///< ask for user decision about EA }; /// the global action for overwriting /// this class is a generic interface to handle what action to perform on both EA and Data /// based on two files to evaluate. class crit_action { public: crit_action() {}; crit_action(const crit_action & ref) = default; crit_action(crit_action && ref) noexcept = default; crit_action & operator = (const crit_action & ref) = default; crit_action & operator = (crit_action && ref) noexcept = default; /// the destructor virtual ~crit_action() = default; /// the action to take based on the files to compare /// \param[in] first is the 'in place' inode /// \param[in] second is the 'to be added' inode /// \param[out] data is the action to perform with file's data /// \param[out] ea is the action to perform with file's EA virtual void get_action(const cat_nomme & first, const cat_nomme & second, over_action_data & data, over_action_ea & ea) const = 0; /// clone construction method /// \return a new object of the same type, /// \note this method must be implemented in all the leaf classes of the /// class crit_action hierarchy virtual crit_action *clone() const = 0; }; /// the basic constant action /// the resulting action is not dependant on the files to compare /// it always returns the action provided through its constructor class crit_constant_action : public crit_action { public: /// the constuctor /// \param[in] data the action to perform on data /// \param[in] ea the action to perform on EA crit_constant_action(over_action_data data, over_action_ea ea) { x_data = data; x_ea = ea; }; crit_constant_action(const crit_constant_action & ref) = default; crit_constant_action & operator = (const crit_constant_action & ref) = default; ~crit_constant_action() = default; /// the inherited pure virtual methods from class action that must be implemented virtual void get_action(const cat_nomme & first, const cat_nomme & second, over_action_data & data, over_action_ea & ea) const override { data = x_data; ea = x_ea; }; virtual crit_action *clone() const override { return new (std::nothrow) crit_constant_action(*this); }; private: over_action_data x_data; over_action_ea x_ea; }; /// the testing class binds criterium to actions /// a testing class is also an action that let the user build complex /// testing. It is thus possible to recursively use testing inside testing class testing : public crit_action { public: /// the constructor /// \param[in] input is the criterium to base the evaluation on /// \param[in] go_true is the action to use for evaluation if the criterium states true /// \param[in] go_false is the action to use for evaluation if the criterium states false testing(const criterium & input, const crit_action & go_true, const crit_action & go_false); testing(const testing & ref) : crit_action(ref) { copy_from(ref); if(!check()) throw Ememory("testing::testing(const testing &)"); }; testing(testing && ref) noexcept : crit_action(std::move(ref)) { nullifyptr(); move_from(std::move(ref)); }; testing & operator = (const testing & ref) { free(); copy_from(ref); if(!check()) throw Ememory("testing::testing(const testing &)"); return *this; }; testing & operator = (testing && ref) noexcept { crit_action::operator = (std::move(ref)); move_from(std::move(ref)); return *this; }; ~testing() { free(); }; /// the inherited pure virtual method from class crit_action that must be implemented virtual void get_action(const cat_nomme & first, const cat_nomme & second, over_action_data & data, over_action_ea & ea) const override { if(x_input->evaluate(first, second)) x_go_true->get_action(first, second, data, ea); else x_go_false->get_action(first, second, data, ea); }; virtual crit_action *clone() const override { return new (std::nothrow) testing(*this); }; private: criterium *x_input; crit_action *x_go_true; crit_action *x_go_false; void nullifyptr() noexcept { x_input = nullptr; x_go_true = x_go_false = nullptr; }; void free() noexcept; void copy_from(const testing & ref); void move_from(testing && ref) noexcept; bool check() const; ///< returns false if an field is nullptr }; /// the crit_chain class sequences crit_actions up to full definition of the action /// several expressions must be added. The first is evaluated, then the second, up to the last /// or up to the step the data_action and ea_action are both fully defined (no data_undefined nor ea_undefined) class crit_chain : public crit_action { public: crit_chain() { sequence.clear(); }; crit_chain(const crit_chain & ref) : crit_action(ref) { copy_from(ref); }; crit_chain(crit_chain && ref) noexcept : crit_action(std::move(ref)) { sequence = std::move(ref.sequence); }; crit_chain & operator = (const crit_chain & ref) { destroy(); copy_from(ref); return *this; }; crit_chain & operator = (crit_chain && ref) noexcept { crit_action::operator = (std::move(ref)); sequence = std::move(ref.sequence); return *this; }; ~crit_chain() { destroy(); }; void add(const crit_action & act); void clear() { destroy(); }; void gobe(crit_chain & to_be_voided); virtual void get_action(const cat_nomme & first, const cat_nomme & second, over_action_data & data, over_action_ea & ea) const override; virtual crit_action *clone() const override { return new (std::nothrow) crit_chain(*this); }; private: std::deque sequence; void destroy(); void copy_from(const crit_chain & ref); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/xz_module.hpp0000644000175000017500000000512714636066467014067 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file xz_module.hpp /// \brief per block encryption using xz algorithm/library /// \ingroup Private /// #ifndef XZ_MODULE_HPP #define XZ_MODULE_HPP extern "C" { #if HAVE_LZMA_H #include #endif } #include "../my_config.h" #include "compress_module.hpp" #include "infinint.hpp" namespace libdar { /// \addtogroup Private /// @{ class xz_module: public compress_module { public: xz_module(U_I compression_level = 9); xz_module(const xz_module & ref) { setup(ref.level); }; xz_module(xz_module && ref) noexcept = default; xz_module & operator = (const xz_module & ref) { end_process(); setup(ref.level); return *this; }; xz_module & operator = (xz_module && ref) noexcept = default; virtual ~xz_module() { end_process(); }; // inherited from compress_module interface virtual compression get_algo() const override { return compression::xz; }; virtual U_I get_max_compressing_size() const override; virtual U_I get_min_size_to_compress(U_I clear_size) const override; virtual U_I compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const override; virtual U_I uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const override; virtual std::unique_ptr clone() const override; private: U_I level; #if LIBLZMA_AVAILABLE mutable lzma_stream lzma_str; #endif void setup(U_I compression_level); void init_decompr() const; void init_compr() const; void end_process() const; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/compile_time_features.hpp0000644000175000017500000001365014636067146016420 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file compile_time_features.hpp /// \brief nested namespace containing routines that give features activated at compile time /// \ingroup API #ifndef COMPILE_TIME_FEATURES_HPP #define COMPILE_TIME_FEATURES_HPP #include "../my_config.h" namespace libdar { /// \addtogroup API /// @{ /// nested namespace inside libdar /// it contains one routine per feature that can be activated or disabled at compile time /// this is to replace the "libdar::get_compile_time_feature" function /// that cannot be updates without breaking backward compatibility namespace compile_time { /// returns whether EA support has been activated at compilation time bool ea() noexcept; /// returns whether largefile (>2GiB) support has been activated at compilation time bool largefile() noexcept; /// returns whether nodump flag support has been activated at compilation time bool nodump() noexcept; /// returns whether special allocation support has been activated at compilation time /// special allocation support brings from a tiny to an important improvement in /// execution time, depending on the number of small files involved in the operation bool special_alloc() noexcept; /// returns the internal integer type used /// \note zero is returned if infinint type is used U_I bits() noexcept; /// returns whether the current libdar is thread safe bool thread_safe() noexcept; /// returns whether libdar is dependent on libz and if so has gzip compression/decompression available bool libz() noexcept; /// returns whether libdar is dependent on libbz2 and if so has bzip2 compression/decompression available bool libbz2() noexcept; /// returns whether libdar is dependent on liblzo and if so has lzo compression/decompression available bool liblzo() noexcept; /// returns whether libdar is dependent on liblxz/liblzma and if so has xz compression/decompression available bool libxz() noexcept; /// returns whether libdar is dependent on libzstd and if so has zstd compression/decompression available bool libzstd() noexcept; /// returns whether libdar is dependen in liblz4 and if so has lz4 compression/decompression available bool liblz4() noexcept; /// returns whether libdar is dependent on libgcrypt and if so has strong encryption and hashing features available bool libgcrypt() noexcept; /// returns whether libdar is dependent on libargon2 and if it has thus argon2 hash algorithm feature available bool libargon2() noexcept; /// returns whether libdar can support furtive read mode when run by privileged user bool furtive_read() noexcept; /// type used to return the endian nature of the current system enum endian { big = 'B', //< big endian little = 'L', //< little endian error = 'E' //< neither big nor little endian! (libdar cannot run on such system) }; /// returns the detected integer endian of the system endian system_endian() noexcept; /// returns true if libdar has support for posix_fadvise activated available bool posix_fadvise() noexcept; /// returns whether libdar has been built with speed optimization for last directory bool fast_dir() noexcept; /// returns whether libdar has been built with support for linux ext2/3/4 FSA bool FSA_linux_extX() noexcept; /// returns whether libdar has been built with support for HFS+ FSA bool FSA_birthtime() noexcept; /// returns whether libdar has been built with support for Linux statx() bool Linux_statx() noexcept; /// returns whether libdar is able to read timestamps at least at microsecond accuracy bool microsecond_read() noexcept; /// returns whether libdar is able to read timestamps at least at nanosecond accuracy bool nanosecond_read() noexcept; /// returns whether libdar is able to write timestamps at least at microsecond accuracy bool microsecond_write() noexcept; /// returns whether libdar is able to write timestamps at least at nanosecond accuracy bool nanosecond_write() noexcept; /// returns whether libdar is able to restore dates of symlinks bool symlink_restore_dates() noexcept; /// returns whether public key cipher (relying on gpgme) are available bool public_key_cipher() noexcept; /// returns whether libthreadar linking will be done, allowing libdar to span several threads bool libthreadar() noexcept; /// return libthreadar version or empty string libthreadar is not available std::string libthreadar_version() noexcept; /// returns whether delta compression is available and delta diff stuff with it bool librsync() noexcept; /// returns whether remote repository feature is available (implemented using libcurl) bool remote_repository() noexcept; /// returns the libcurl version used or empty string if not available std::string libcurl_version() noexcept; } // end of compile_time namespace /// @} } // end of libdar namespace #endif dar-2.7.15/src/libdar/heap.hpp0000644000175000017500000000645114636066467012777 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file heap.hpp /// \brief heap data structure (relying on FIFO) /// \ingroup Private /// #ifndef HEAP_HPP #define HEAP_HPP #include "../my_config.h" #include #include "integers.hpp" #include #include #if HAVE_LIBTHREADAR_LIBTHREADAR_HPP #include #endif namespace libdar { /// \addtogroup Private /// @{ /// the class heap is nothing related to the common heap datastructure this is just a "heap" in the sense of a pool of preallocated objects template class heap { public: heap() {}; // start with an empty heap heap(const heap & ref) = delete; heap(heap && ref) = default; heap & operator = (const heap & ref) = delete; heap & operator = (heap && ref) noexcept = default; std::unique_ptr get(); void put(std::unique_ptr && obj); void put(std::deque > & list); U_I get_size() const { return tas.size(); }; private: std::deque > tas; #ifdef LIBTHREADAR_AVAILABLE libthreadar::mutex access; #endif }; template std::unique_ptr heap::get() { std::unique_ptr ret; #ifdef LIBTHREADAR_AVAILABLE access.lock(); try { #endif if(tas.empty()) throw Erange("heap::get", "heap is empty, it should have be set larger"); ret = std::move(tas.back()); // moving the object pointed to by tas.back() to ret tas.pop_back(); // removing the now empty pointer at the end of 'tas' #ifdef LIBTHREADAR_AVAILABLE } catch(...) { access.unlock(); throw; } access.unlock(); #endif return ret; } template void heap::put(std::unique_ptr && obj) { #ifdef LIBTHREADAR_AVAILABLE access.lock(); try { #endif tas.push_back(std::move(obj)); #ifdef LIBTHREADAR_AVAILABLE } catch(...) { access.unlock(); throw; } access.unlock(); #endif } template void heap::put(std::deque > & list) { typename std::deque >::iterator it = list.begin(); #ifdef LIBTHREADAR_AVAILABLE access.lock(); try { #endif while(it != list.end()) { tas.push_back(std::move(*it)); ++it; } #ifdef LIBTHREADAR_AVAILABLE } catch(...) { access.unlock(); throw; } access.unlock(); #endif } /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_file.hpp0000644000175000017500000003210014636066467013616 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_file.hpp /// \brief class used to record plain files in a catalogue /// \ingroup Private #ifndef CAT_FILE_HPP #define CAT_FILE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_inode.hpp" #include "memory_file.hpp" #include "cat_delta_signature.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the plain file class class cat_file : public cat_inode { public : /// how to get data from archive enum get_data_mode { keep_compressed, ///< provide access to compressed data keep_hole, ///< provide access to uncompressed data but sparse_file datastructure normal, ///< provide access to full data (uncompressed, uses skip() to restore holes) plain ///< provide access to plain data, no skip to restore holes, provide instead zeroed bytes }; static constexpr U_8 FILE_DATA_WITH_HOLE = 0x01; ///< file's data contains hole datastructure static constexpr U_8 FILE_DATA_IS_DIRTY = 0x02; ///< data modified while being saved static constexpr U_8 FILE_DATA_HAS_DELTA_SIG = 0x04; ///< delta signature is present cat_file(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & src, const path & che, const infinint & taille, const infinint & fs_device, bool x_furtive_read_mode); cat_file(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, compression default_algo, bool small); cat_file(const cat_file & ref); cat_file(cat_file && ref) = delete; cat_file & operator = (const cat_file & ref) = delete; cat_file & operator = (cat_file && ref) = delete; ~cat_file() { detruit(); }; virtual bool has_changed_since(const cat_inode & ref, const infinint & hourshift, comparison_fields what_to_check) const override; infinint get_size() const { return *size; }; void change_size(const infinint & s) const { *size = s; }; infinint get_storage_size() const { return *storage_size; }; void set_storage_size(const infinint & s) { *storage_size = s; }; /// check whether the object will be able to provide a object using get_data() method bool can_get_data() const { return get_saved_status() == saved_status::saved || get_saved_status() == saved_status::delta || status == from_path; }; /// returns a newly allocated object in read_only mode /// \param[in] mode whether to return compressed, with hole or plain file /// \param[in,out] delta_sig_mem if not nullptr, write to that file the delta signature of the file /// \param[in] signature_block_size is the block size to use to build the signature (passed to librsync as is) /// \param[in] delta_ref if not nullptr, use the provided signature to generate a delta binary /// \param[in] checksum if not null will set *checsum to the address of a newly allocated crc object /// that the caller has the duty to release when no more needed but *not before* the returned generic_file /// object has been destroyed first. The computed crc is against the /// real data found on disk not the one of the delta diff that could be generated from get_data() /// \note the object pointed to by delta_sig must exist during the whole life of the returned /// object, as well as the object pointed to by delta_ref if provided. /// \note when both delta_sig and delta_ref are provided, the delta signature is computed on the /// file data, then the delta binary is computed. virtual generic_file *get_data(get_data_mode mode, std::shared_ptr delta_sig_mem, U_I signature_block_size, std::shared_ptr delta_ref, const crc **checksum = nullptr) const; void clean_data(); // partially free memory (but get_data() becomes disabled) void set_offset(const infinint & r); const infinint & get_offset() const; virtual unsigned char signature() const override { return 'f'; }; virtual std::string get_description() const override { return "file"; }; void set_crc(const crc &c); bool get_crc(const crc * & c) const; ///< the argument is set the an allocated crc object the owned by the "cat_file" object, its stay valid while this "cat_file" object exists and MUST NOT be deleted by the caller in any case bool has_crc() const { return check != nullptr; }; bool get_crc_size(infinint & val) const; ///< returns true if crc is know and puts its width in argument void drop_crc() { if(check != nullptr) { delete check; check = nullptr; } }; // whether the plain file has to detect sparse file void set_sparse_file_detection_read(bool val) { if(status == from_cat) throw SRC_BUG; if(val) file_data_status_read |= FILE_DATA_WITH_HOLE; else file_data_status_read &= ~FILE_DATA_WITH_HOLE; }; void set_sparse_file_detection_write(bool val) { if(val) file_data_status_write |= FILE_DATA_WITH_HOLE; else file_data_status_write &= ~FILE_DATA_WITH_HOLE; }; // whether the plain file is stored with a sparse_file datastructure in the archive bool get_sparse_file_detection_read() const { return (file_data_status_read & FILE_DATA_WITH_HOLE) != 0; }; bool get_sparse_file_detection_write() const { return (file_data_status_write & FILE_DATA_WITH_HOLE) != 0; }; virtual cat_entree *clone() const override { return new (std::nothrow) cat_file(*this); }; compression get_compression_algo_read() const { return algo_read; }; compression get_compression_algo_write() const { return algo_write; }; // object migration methods (merging) void change_compression_algo_write(compression x) { algo_write = x; }; // dirtiness bool is_dirty() const { return dirty; }; void set_dirty(bool value) { dirty = value; }; /// return whether the object has an associated delta signature structure bool has_delta_signature_structure() const { return delta_sig != nullptr; }; /// return whether the object has an associated delta signature structure including a delta signature data (not just CRC) /// \note when reading file from archive/generic_file if the metadata is not loaded to memory calling /// either read_delta_signature_metadata() or read_delta_signature() *and* if sequential read mode is used /// this call will always report false, even if delta signature can be available from filesystem/archive bool has_delta_signature_available() const { return delta_sig != nullptr && delta_sig->can_obtain_sig(); }; /// returns whether the object has a base patch CRC (s_delta status objects) bool has_patch_base_crc() const; /// returns the CRC of the file to base the patch on, for s_delta objects bool get_patch_base_crc(const crc * & c) const; /// set the reference CRC of the file to base the patch on, for s_detla objects void set_patch_base_crc(const crc & c); /// returns whether the object has a CRC corresponding to data (for s_saved, s_delta, and when delta signature is present) bool has_patch_result_crc() const; /// returns the CRC the file will have once restored or patched (for s_saved, s_delta, and when delta signature is present) bool get_patch_result_crc(const crc * & c) const; /// set the CRC the file will have once restored or patched (for s_saved, s_delta, and when delta signature is present) void set_patch_result_crc(const crc & c); /// prepare the object to receive a delta signature structure void will_have_delta_signature_structure(); /// prepare the object to receive a delta signature structure including delta signature /// this calls will lead an to error if the delta_signature is written to archive or used while only CRC info /// has been set (= metadata of delta signature) but no delta signature data has read from the archive or /// has been provided (by mean of a memory_file when calling dump_delta_signature() method) void will_have_delta_signature_available(); /// write down to archive the given delta signature /// \param[in] sig is the signature to dump /// \param[in] sign_block_size block size to used to build the delta signature /// \param[in] where is the location where to write down the signature /// \param[in] small if set to true drop down additional information to allow sequential reading mode void dump_delta_signature(std::shared_ptr & sig, U_I sign_block_size, generic_file & where, bool small) const; /// variant of dump_delta_signature when just CRC have to be dumped void dump_delta_signature(generic_file & where, bool small) const; /// load metadata (and delta signature when in sequential mode) into memory /// \note call drop_delta_signature_data() subsequently if only the metada is needed (when un sequential read mode or not, it does not hurt) void read_delta_signature_metadata() const; /// fetch the delta signature from the archive /// \param[out] delta_sig is either nullptr or points to a shared memory_file /// containing the delta signature. /// \param[out] block_len is the block size that has been used to build the signature /// \note nullptr is returned if the delta_signature only contains CRCs void read_delta_signature(std::shared_ptr & delta_sig, U_I & block_len) const; /// drop the delta signature from memory (will not more be posible to be read, using read_delta_signature) void drop_delta_signature_data() const; /// return true if ref and "this" have both equal delta signatures bool has_same_delta_signature(const cat_file & ref) const; /// remove information about delta signature also associated CRCs if status is not s_delta void clear_delta_signature_only(); /// remove any information about delta signature void clear_delta_signature_structure(); /// not used virtual bool operator == (const cat_entree & ref) const override { return true; }; /// compare just data not inode information EA nor FSA bool same_data_as(const cat_file & other, bool check_data, const infinint & hourshift); /// expose the archive format the object of the backup this object comes from const archive_version & get_archive_version() const { return read_ver; }; protected: virtual void sub_compare(const cat_inode & other, bool isolated_mode) const override; virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; virtual void post_constructor(const pile_descriptor & pdesc) override; enum { empty, from_path, from_cat } status; private: std::string chemin; ///< path to the data (when read from filesystem) infinint *offset; ///< start location of the data in 'loc' infinint *size; ///< size of the data (uncompressed) infinint *storage_size; ///< how much data used in archive (after compression) crc *check; ///< crc computed on the data bool dirty; ///< true when a file has been modified at the time it was saved compression algo_read; ///< which compression algorithm to use to read the file's data compression algo_write; ///< which compression algorithm to use to write down (merging) the file's data bool furtive_read_mode; ///< used only when status equals "from_path" char file_data_status_read; ///< defines the datastructure to use when reading the data char file_data_status_write; ///< defines the datastructure to apply when writing down the data crc *patch_base_check; ///< when data contains a delta patch, moved from delta_sig since format 10.2 cat_delta_signature *delta_sig; ///< delta signature and associated CRC mutable bool delta_sig_read; ///< whether delta sig has been read/initialized from filesystem archive_version read_ver; ///< archive format used/to use void sub_compare_internal(const cat_inode & other, bool can_read_my_data, bool can_read_other_data, const infinint & hourshift) const; void clean_patch_base_crc(); void detruit(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_ids.hpp0000644000175000017500000001000114636066467015067 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filesystem_ids.hpp /// \brief gather the ids of different filesystem to provide a filter based on filesystem /// \ingroup Private #ifndef FILESYSTEM_IDS_HPP #define FILESYSTEM_IDS_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include #include "infinint.hpp" #include "path.hpp" namespace libdar { /// \addtogroup Private /// @{ class filesystem_ids { public: /// set the filesystem to be taken as root filesystem /// \param[in] root the filesystem which will always be covered filesystem_ids(const path & root); /// mimics the old boolean flag, /// \param[in] same_fs if set to false is_covered() always returns true /// \param[in] root_fs the filesystem in which the given path points to is /// considered as the root filesystem, if same_fs is set to true, no file /// outside this filesystem will be saved /// \note if same_fs is set to false, is_covered() always return true /// and root_fs is not used filesystem_ids(bool same_fs, const path & root_fs); filesystem_ids(const filesystem_ids & ref) = default; filesystem_ids(filesystem_ids && ref) noexcept = default; filesystem_ids & operator = (const filesystem_ids & ref) = default; filesystem_ids & operator = (filesystem_ids && ref) noexcept = default; ~filesystem_ids() = default; /// need when using the boolean constructor /// \note this method has no impact if the constructor /// provided the root_fs, it is however mandatory to call /// it when using the boolean constructor void change_root_fs(const path & root); /// include the filesystem where the given path is stored /// \note if no fs is included, the filesystem_ids() behaves /// like a blacklist, is_covered() will return true except for /// files located on the explictely excluded filesystems. void include_fs_at(const path & chem); /// exclude the filessytem where the given path is stored /// \note if a filesystem is both included and excluded /// it is excluded. The only filesystem that is never /// excluded is the one set as root filesystem /// \note if no fs is excluded, the filesystem_ids() behaves /// like a whitelist: is_covered() will return true only for the /// files located on the explictely included filesystems /// \note if both include and excluded list are empty, /// is_covered() always return true. void exclude_fs_at(const path & chem); /// returns true if the fs_id is included and not excluded, /// true is also always returned for the fs set as root_fs bool is_covered(const infinint & fs_id) const; /// return true if the path points to a filesystem that is covered bool is_covered(const path & chem) const; /// reset the filesystem_ids object void clear() { included.clear(); excluded.clear(); }; private: infinint root_fs; std::set included; std::set excluded; static infinint path2fs_id(const std::string & path); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/scrambler.cpp0000644000175000017500000000542314636066467014025 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "scrambler.hpp" using namespace std; namespace libdar { scrambler::scrambler(const secu_string & pass, generic_file & hidden_side) : generic_file(hidden_side.get_mode()) { if(pass.get_size() == 0) throw Erange("scrambler::scrambler", gettext("Key cannot be an empty string")); key = pass; len = key.get_size(); ref = & hidden_side; buffer = nullptr; buf_size = 0; } U_I scrambler::inherited_read(char *a, U_I size) { if(ref == nullptr) throw SRC_BUG; unsigned char *ptr = (unsigned char *)a; U_32 index = ref->get_position() % len; U_I ret = ref->read(a, size); for(U_I i = 0; i < ret; ++i) { ptr[i] = ((S_I)(ptr[i]) - (unsigned char)(key[index])) % 256; index = (index + 1)%len; } return ret; } void scrambler::inherited_write(const char *a, U_I size) { const unsigned char *ptr = (const unsigned char *)a; if(ref == nullptr) throw SRC_BUG; U_32 index = ref->get_position() % len; if(size > buf_size) { if(buffer != nullptr) { delete [] buffer; buffer = nullptr; } buffer = new (nothrow) unsigned char[size]; if(buffer != nullptr) buf_size = size; else { buf_size = 0; throw Ememory("scramble::inherited_write"); } } for(U_I i = 0; i < size; ++i) { buffer[i] = (ptr[i] + (unsigned char)(key[index])) % 256; index = (index + 1)%len; } ref->write((char *)buffer, size); } } // end of namespace dar-2.7.15/src/libdar/compression.cpp0000644000175000017500000001013314636066467014406 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_CTYPE_H #include #endif } // end extern "C" #include "compression.hpp" #include "erreurs.hpp" #include "tools.hpp" using namespace std; namespace libdar { compression char2compression(char a) { switch(a) { case 'n': return compression::none; case 'z': case 'Z': return compression::gzip; case 'y': case 'Y': return compression::bzip2; case 'j': case 'J': return compression::lzo1x_1_15; case 'k': case 'K': return compression::lzo1x_1; case 'l': case 'L': return compression::lzo; case 'x': case 'X': return compression::xz; case 'd': case 'D': return compression::zstd; case 'q': case 'Q': return compression::lz4; default : throw Erange("char2compression", gettext("unknown compression")); } } bool char2compression_mode(char a) { return isupper(a); } char compression2char(compression c, bool per_block) { switch(c) { case compression::none: return per_block ? 'N': 'n'; case compression::gzip: return per_block ? 'Z': 'z'; case compression::bzip2: return per_block ? 'Y': 'y'; case compression::lzo: return per_block ? 'L': 'l'; case compression::xz: return per_block ? 'X': 'x'; case compression::lzo1x_1_15: return per_block ? 'J': 'j'; case compression::lzo1x_1: return per_block ? 'K': 'k'; case compression::zstd: return per_block ? 'D': 'd'; case compression::lz4: return per_block ? 'Q': 'q'; default: throw Erange("compression2char", gettext("unknown compression")); } } string compression2string(compression c) { switch(c) { case compression::none: return "none"; case compression::gzip: return "gzip"; case compression::bzip2: return "bzip2"; case compression::lzo: return "lzo"; case compression::xz: return "xz"; case compression::lzo1x_1_15: return "lzop-1"; case compression::lzo1x_1: return "lzop-3"; case compression::zstd: return "zstd"; case compression::lz4: return "lz4"; default: throw Erange("compresion2string", gettext("unknown compression")); } } compression string2compression(const std::string & a) { if(a == "gzip" || a == "gz") return compression::gzip; if(a == "bzip2" || a == "bzip" || a == "bz") return compression::bzip2; if(a == "lzo" || a == "lz" || a == "l") return compression::lzo; if(a == "lzop-1" || a == "lzop1") return compression::lzo1x_1_15; if(a == "lzop-3" || a == "lzop3") return compression::lzo1x_1; if(a == "xz" || a == "lzma") return compression::xz; if(a == "zstd") return compression::zstd; if(a == "lz4") return compression::lz4; if(a == "none") return compression::none; throw Erange("string2compression", tools_printf(gettext("unknown compression algorithm: %S"), &a)); } } // end of namespace dar-2.7.15/src/libdar/fichier_local.hpp0000644000175000017500000001225614636066467014645 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file fichier_local.hpp /// \brief class fichier_local definition. This is a full implementation/inherited class of class fichier_global /// this type of object are generated by entrepot_local. /// \ingroup Private #ifndef FICHIER_LOCAL_HPP #define FICHIER_LOCAL_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "integers.hpp" #include "user_interaction.hpp" #include "fichier_global.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// filesystem local files class fichier_local : public fichier_global { public : /// full featured constructors fichier_local(const std::shared_ptr & dialog, const std::string & chemin, gf_mode m, U_I permission, bool fail_if_exists, bool erase, bool furtive_mode); /// constructor without user_interaction /// \note can only build a read-only object fichier_local(const std::string & chemin, bool furtive_mode = false); /// copy constructor fichier_local(const fichier_local & ref) : fichier_global(ref) { copy_from(ref); }; /// move constructor fichier_local(fichier_local && ref) noexcept: fichier_global(std::move(ref)) { move_from(std::move(ref)); }; /// assignment operator fichier_local & operator = (const fichier_local & ref) { detruit(); copy_from(ref); return *this; }; /// move operator fichier_local & operator = (fichier_local && ref) noexcept { fichier_global::operator = (std::move(ref)); move_from(std::move(ref)); return *this; }; /// destructor ~fichier_local() { detruit(); }; /// set the ownership of the file virtual void change_ownership(const std::string & user, const std::string & group) override; /// change the permission of the file virtual void change_permission(U_I perm) override; /// return the size of the file virtual infinint get_size() const override; /// set posix_fadvise for the whole file virtual void fadvise(advise adv) const override; // inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override { return true; }; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return true; }; virtual infinint get_position() const override; /// provide the low level filedescriptor to the call and terminate() /// \note this is the caller duty to close() the provided filedescriptor S_I give_fd_and_terminate() { int ret = filedesc; filedesc = -1; terminate(); return ret; }; protected : // inherited from generic_file grand-parent class virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_read_ahead(const infinint & amount) override {}; // nothing done, calling readahead(2) could be added in the future virtual void inherited_sync_write() override { fsync(); }; virtual void inherited_flush_read() override {}; // nothing stored in transit in this object virtual void inherited_terminate() override { if(adv == advise_dontneed) fadvise(adv); }; // inherited from fichier_global parent class virtual U_I fichier_global_inherited_write(const char *a, U_I size) override; virtual bool fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) override; private : S_I filedesc; advise adv; void open(const std::string & chemin, gf_mode m, U_I permission, bool fail_if_exists, bool erase, bool furtive_mode); void copy_from(const fichier_local & ref); void move_from(fichier_local && ref) noexcept; void detruit() { if(filedesc >= 0) close(filedesc); filedesc = -1; }; int advise_to_int(advise arg) const; /// sync the data to disk /// this is necessary under Linux for better efficiency /// before calling fadvise(advise_dontneed) else write pending blocks /// would stay in the cache more time than necessary void fsync() const; off_t get_eof_offset() const; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/zapette_protocol.cpp0000644000175000017500000001020614636066467015443 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif } // end extern "C" #include #include #include "zapette_protocol.hpp" #include "tools.hpp" using namespace std; namespace libdar { void request::write(generic_file *f) { U_16 tmp = htons(size); f->write(&serial_num, 1); offset.dump(*f); f->write((char *)&tmp, sizeof(tmp)); if(size == REQUEST_SIZE_SPECIAL_ORDER && offset == REQUEST_OFFSET_CHANGE_CONTEXT_STATUS) tools_write_string(*f, info); } void request::read(generic_file *f) { U_16 tmp; U_16 pas; if(f->read(&serial_num, 1) == 0) throw Erange("request::read", gettext("Partial request received, aborting\n")); if(f == nullptr) throw SRC_BUG; offset = infinint(*f); pas = 0; while(pas < sizeof(tmp)) pas += f->read((char *)&tmp+pas, sizeof(tmp)-pas); size = ntohs(tmp); if(size == REQUEST_SIZE_SPECIAL_ORDER && offset == REQUEST_OFFSET_CHANGE_CONTEXT_STATUS) tools_read_string(*f, info); else info = ""; } void answer::write(generic_file *f, char *data) { U_16 tmp = htons(size); f->write(&serial_num, 1); f->write(&type, 1); switch(type) { case ANSWER_TYPE_DATA: f->write((char *)&tmp, sizeof(tmp)); if(data != nullptr) f->write(data, size); else if(size != 0) throw SRC_BUG; break; case ANSWER_TYPE_INFININT: arg.dump(*f); break; default: throw SRC_BUG; } } void answer::read(generic_file *f, char *data, U_16 max) { U_16 tmp; U_16 pas; f->read(&serial_num, 1); f->read(&type, 1); switch(type) { case ANSWER_TYPE_DATA: pas = 0; while(pas < sizeof(tmp)) pas += f->read((char *)&tmp+pas, sizeof(tmp)-pas); size = ntohs(tmp); // recycling tmp to carry the max data to store in the data arg of this method if(size > max) tmp = max; else tmp = size; // recycling pas to follow the steps of data fullfilment pas = 0; while(pas < tmp) pas += f->read(data+pas, tmp - pas); // need to drop the remaining data if(size > max) { char black_hole; for(tmp = max; tmp < size; ++tmp) f->read(&black_hole, 1); // might not be very performant code } arg = 0; break; case ANSWER_TYPE_INFININT: if(f == nullptr) throw SRC_BUG; arg = infinint(*f); size = 0; break; default: throw Erange("answer::read", gettext("Corrupted data read on pipe")); } } } // end of namespace dar-2.7.15/src/libdar/generic_file_overlay_for_gpgme.hpp0000644000175000017500000000525014636066467020257 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file generic_file_overlay_for_gpgme.hpp /// \brief adaptation class from gpgme data buffer to libdar generic_file interface /// \ingroup Private #ifndef GENERIC_FILE_OVERLAY_FOR_GPGME_HPP #define GENERIC_FILE_OVERLAY_FOR_GPGME_HPP extern "C" { #if HAVE_GPGME_H #include #endif } #include "../my_config.h" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// generic_file interface for for gpgme class generic_file_overlay_for_gpgme { public: /// create a gpgme data buffer of the given generic_file /// \param[in] f is a pointer to an existing generic_file that must exist during the whole live of this overlay object generic_file_overlay_for_gpgme(generic_file *f); /// no copy constructor allowed generic_file_overlay_for_gpgme(const generic_file_overlay_for_gpgme & ref) = delete; /// no move constructor allowed generic_file_overlay_for_gpgme(generic_file_overlay_for_gpgme && re) noexcept = delete; /// no asignment operator generic_file_overlay_for_gpgme & operator = (const generic_file_overlay_for_gpgme & ref) = delete; /// no move operator generic_file_overlay_for_gpgme & operator = (generic_file_overlay_for_gpgme && ref) noexcept = delete; #ifdef GPGME_SUPPORT /// destructor ~generic_file_overlay_for_gpgme() { gpgme_data_release(handle); }; /// return the handle to be used by gpgme to drive the generic_file given at constructor time gpgme_data_t get_gpgme_handle() const { return handle; }; generic_file *get_below() { return below; }; private: generic_file *below; gpgme_data_t handle; gpgme_data_cbs cbs; #endif }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/storage.hpp0000644000175000017500000002170214636066467013522 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file storage.hpp /// \brief contains a class that permits arbitrary large data storage /// \ingroup Private /// \note API included module due to dependencies #ifndef STORAGE_HPP #define STORAGE_HPP #include "../my_config.h" #include "integers.hpp" #include "proto_generic_file.hpp" #if LIBDAR_MODE == 32 || LIBDAR_MODE == 64 #include "infinint.hpp" #else namespace libdar { class infinint; } #endif namespace libdar { /// \addtogroup Private /// @{ /// arbitrary large storage structure /// used to store infinint class storage { private: struct cellule { cellule() : next(nullptr), prev(nullptr), data(nullptr), size(0) {}; struct cellule *next, *prev; unsigned char *data; U_32 size; }; public: storage(U_32 size) { make_alloc(size, first, last); }; storage(const infinint & size); storage(proto_generic_file & f, const infinint & size); storage(const storage & ref) { copy_from(ref); }; storage(storage && ref) noexcept: first(nullptr), last(nullptr) { move_from(std::move(ref)); }; storage & operator = (const storage & val) { detruit(first); copy_from(val); return *this; }; storage & operator = (storage && val) noexcept { move_from(std::move(val)); return *this; }; ~storage() { detruit(first); }; bool operator < (const storage & ref) const noexcept { return difference(ref) < 0; }; // true if arg uses more space than this bool operator == (const storage & ref) const noexcept { return difference(ref) == 0; }; // true if arg have same space than this bool operator > (const storage & ref) const noexcept { return difference(ref) > 0; }; bool operator <= (const storage & ref) const noexcept { return difference(ref) <= 0; }; bool operator >= (const storage & ref) const noexcept { return difference(ref) >= 0; }; bool operator != (const storage & ref) const noexcept { return difference(ref) != 0; }; unsigned char & operator [](infinint position); unsigned char operator [](const infinint & position) const; infinint size() const noexcept; void clear(unsigned char val = 0) noexcept; void dump(proto_generic_file & f) const; class iterator { public : iterator() : ref(nullptr), cell(nullptr), offset(0) {}; iterator(const iterator & ref) = default; iterator(iterator && ref) = default; iterator & operator = (const iterator & ref) = default; iterator & operator = (iterator && ref) = default; ~iterator() = default; iterator operator ++ (S_I x) { iterator ret = *this; skip_plus_one(); return ret; }; iterator operator -- (S_I x) { iterator ret = *this; skip_less_one(); return ret; }; iterator & operator ++ () { skip_plus_one(); return *this; }; iterator & operator -- () { skip_less_one(); return *this; }; iterator operator + (U_32 s) const { iterator ret = *this; ret += s; return ret; }; iterator operator - (U_32 s) const { iterator ret = *this; ret -= s; return ret; }; iterator & operator += (U_32 s); iterator & operator -= (U_32 s); unsigned char &operator *() const; void skip_to(const storage & st, infinint val); // absolute position in st infinint get_position() const; bool operator == (const iterator & cmp) const noexcept { return ref == cmp.ref && cell == cmp.cell && offset == cmp.offset; }; bool operator != (const iterator & cmp) const noexcept { return ! (*this == cmp); }; private: static constexpr U_32 OFF_BEGIN = 1; static constexpr U_32 OFF_END = 2; const storage *ref; struct cellule *cell; U_32 offset; void relative_skip_to(S_32 val); bool points_on_data() const noexcept { return ref != nullptr && cell != nullptr && offset < cell->size; }; inline void skip_plus_one(); inline void skip_less_one(); friend class storage; }; // public storage methode using iterator iterator begin() const { iterator ret; ret.cell = first; if(ret.cell != nullptr) ret.offset = 0; else ret.offset = iterator::OFF_END; ret.ref = this; return ret; }; iterator end() const { iterator ret; ret.cell = nullptr; ret.offset = iterator::OFF_END; ret.ref = this; return ret; }; // WARNING for the two following methods : // there is no "reverse_iterator" type, unlike the standart lib, // thus when going from rbegin() to rend(), you must use the -- operator // unlike the stdlib, that uses the ++ operator. this is the only difference in use with stdlib. iterator rbegin() const { iterator ret; ret.cell = last; ret.offset = last != nullptr ? last->size-1 : 0; ret.ref = this; return ret; }; iterator rend() const { iterator ret; ret.cell = nullptr, ret.offset = iterator::OFF_BEGIN; ret.ref = this; return ret; }; /// write data to the storage at the location pointed to by it /// \param[in,out] it where to write data to, at the end this iterator points just after the data that has been wrote /// \param[in] a gives to the address where is located the data to write to the storage object /// \param[in] size how much bytes to write to the storage U_I write(iterator & it, unsigned char *a, U_I size); U_I read(iterator & it, unsigned char *a, U_I size) const; bool write(iterator & it, unsigned char a) { return write(it, &a, 1) == 1; }; bool read(iterator & it, unsigned char &a) const { return read(it, &a, 1) == 1; }; // after one of these 3 calls, the iterator given in argument are undefined (they may point nowhere) void insert_null_bytes_at_iterator(iterator it, U_I size); void insert_const_bytes_at_iterator(iterator it, unsigned char a, U_I size); void insert_bytes_at_iterator(iterator it, unsigned char *a, U_I size); void insert_as_much_as_necessary_const_byte_to_be_as_wider_as(const storage & ref, const iterator & it, unsigned char value); void remove_bytes_at_iterator(iterator it, U_I number); void remove_bytes_at_iterator(iterator it, infinint number); void truncate(const infinint & pos); private: struct cellule *first, *last; void copy_from(const storage & ref); void move_from(storage && ref) noexcept; S_32 difference(const storage & ref) const; void reduce(); // heuristic that tries to free some memory; void insert_bytes_at_iterator_cmn(iterator it, bool constant, unsigned char *a, U_I size); void fusionne(struct cellule *a_first, struct cellule *a_last, struct cellule *b_first, struct cellule *b_last, struct cellule *&res_first, struct cellule * & res_last); static void detruit(struct cellule *c); // destroy all cells following 'c' including 'c' itself static void make_alloc(U_32 size, struct cellule * & begin, struct cellule * & end); static void make_alloc(infinint size, cellule * & begin, struct cellule * & end); friend class storage::iterator; }; inline void storage::iterator::skip_plus_one() { if(cell != nullptr) if(++offset >= cell->size) { cell = cell->next; if(cell != nullptr) offset = 0; else offset = OFF_END; } } inline void storage::iterator::skip_less_one() { if(cell != nullptr) { if(offset > 0) --offset; else { cell = cell->prev; if(cell != nullptr) offset = cell->size - 1; else offset = OFF_BEGIN; } } } /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive_aux.hpp0000644000175000017500000000607014636067146014350 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive_aux.hpp /// \brief set of datastructures used to interact with a catalogue object /// \ingroup API #ifndef ARCHIVE_AUX_HPP #define ARCHIVE_AUX_HPP #include "../my_config.h" #include "integers.hpp" #include namespace libdar { /// \addtogroup API /// @{ /// how to detect data has changed when some fields enum class modified_data_detection { any_inode_change, ///< historical behavior, resave an inode on any metadata change mtime_size, ///< default behavior since release 2.6.0 resave only if file size of mtime changed }; /// how to consider file change during comparison and incremental backup enum class comparison_fields { all, ///< consider any available field for comparing inodes ignore_owner, ///< consider any available field except ownership fields mtime, ///< consider any available field except ownership and permission fields inode_type ///< only consider the file type }; /// hashing algorithm available enum class hash_algo { none, ///< no hashing algorithm md5, ///< MD5 algorithm sha1, ///< SHA1 algorithm sha512,///< SHA-512 algorithm argon2 ///< argon2 hash algo }; /// convert hash value to human readable string extern std::string hash_algo_to_string(hash_algo algo); /// convert string to hash algorithm /// \param[in] arg string to cast to a hash algorithm name /// \param[out] val is set to the corresponding hash algorithm if the function returns true /// \return true if provided string matches a valid hash algoritm name and give the hash_algo in argument extern bool string_to_hash_algo(const std::string & arg, hash_algo & val); /// convert hash value to libgcrypt hash value extern U_I hash_algo_to_gcrypt_hash(hash_algo algo); /// convert hash value to char value extern unsigned char hash_algo_to_char(hash_algo algo); /// convert char to hash_algo /// \note throw Erange exception if provided char is invalid extern hash_algo char_to_hash_algo(unsigned char arg); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/escape_catalogue.cpp0000644000175000017500000006521414636067146015336 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "escape_catalogue.hpp" #include "macro_tools.hpp" #include "cat_all_entrees.hpp" #include "tools.hpp" using namespace std; namespace libdar { escape_catalogue::escape_catalogue(const shared_ptr & dialog, const pile_descriptor & x_pdesc, const datetime & root_last_modif, const label & data_name) : catalogue(dialog, root_last_modif, data_name) { set_esc_and_stack(x_pdesc); x_ver.set_edition(macro_tools_supported_version); x_ver.set_compression_algo(compression::none); x_lax = false; corres.clear(); status = ec_completed; // yes, with that constructor, the catalogue contains all known object and entree do not miss any field cat_det = nullptr; min_read_offset = 0; depth = 0; // we start at the root... of course wait_parent_depth = 0; // to disable this feature // dropping the data_name in the archive pdesc->stack->sync_write_above(pdesc->esc); // esc is now up to date pdesc->esc->add_mark_at_current_position(escape::seqt_data_name); data_name.dump(*(pdesc->esc)); } escape_catalogue::escape_catalogue(const shared_ptr & dialog, const pile_descriptor & x_pdesc, const header_version & ver, const list & known_signatories, bool lax) : catalogue(dialog, datetime(0), label_zero) { set_esc_and_stack(x_pdesc); x_ver = ver; known_sig = known_signatories; x_lax = lax; corres.clear(); status = ec_init; // with that constructor, the catalogue starts empty and get completed entry by entry each time a one asks for its contents (read() method) cat_det = nullptr; min_read_offset = 0; depth = 0; // we start at the root wait_parent_depth = 0; // to disable this feature // fetching the value of ref_data_name pdesc->stack->flush_read_above(pdesc->esc); if(pdesc->esc->skip_to_next_mark(escape::seqt_data_name, false)) { // we are at least at revision "08" of archive format, or this is a collision with data of an old archive or of an archive without escape marks label tmp; try { tmp.read(*(pdesc->esc)); set_data_name(tmp); } catch(...) { if(!lax) throw Erange("escape_catalogue::escape_catalogue", gettext("incoherent data after escape sequence, cannot read internal data set label")); else { get_ui().message("LAX MODE: Could not read the internal data set label, using a fake value, this will probably avoid using isolated catalogue"); set_data_name(label_zero); } } } else // skip to expected mark failed if(!lax) throw Erange("escape_catalogue::escape_catalogue", gettext("Could not find tape mark for the internal catalogue")); else { contextual *cont_data = nullptr; pdesc->stack->find_first_from_bottom(cont_data); get_ui().message("LAX MODE: Could not read the internal data set label, using a fake value, this will probably avoid using isolated catalogue"); if(cont_data == nullptr) set_data_name(label_zero); else set_data_name(cont_data->get_data_name()); } // considering the in_place path if(x_ver.get_edition() >= archive_version(11,1)) { if(pdesc->esc->skip_to_next_mark(escape::seqt_in_place, false)) { string tmp_s; path in_place("."); try { tools_read_string(*(pdesc->esc), tmp_s); in_place = path(tmp_s); if(in_place.is_relative()) if(in_place == path(".")) catalogue::clear_in_place(); else throw Erange("escape_catalogue::escape_catalogue", gettext("invalid in-place path value")); else catalogue::set_in_place(in_place); } catch(Egeneric & e) { if(!lax) { e.prepend_message(gettext("Error while reading in-place path using tape marks")); throw; } else { get_ui().message(gettext("LAX MODE: Could not read in-place path information, assuming corruption occurred or no in-path place was present")); catalogue::clear_in_place(); } } catch(...) { if(!lax) throw SRC_BUG; else { get_ui().message(gettext("LAX MODE: unexpected exception met when reading in-path place using tape marks, ignoring in-path info and continuing")); catalogue::clear_in_place(); } } } else // failed finding the in_place path { if(!lax) throw Erange("escape_catalogue::escape_catalogue", gettext("Could not find tape mark for the in-place path")); else { get_ui().message("LAX MODE: Could not find the in-place path information as it should be found for this archive format, assuming it no in-place path is present so for older format"); catalogue::clear_in_place(); } } } } escape_catalogue & escape_catalogue::operator = (const escape_catalogue &ref) { catalogue *me = this; const catalogue *you = &ref; destroy(); // copying the catalogue part *me = *you; // copying the escape_catalogue specific part copy_from(ref); return *this; } void escape_catalogue::pre_add(const cat_entree *ref, const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(where->esc == nullptr) throw SRC_BUG; where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_file); ref->dump(*where, true); } void escape_catalogue::pre_add_ea(const cat_entree *ref, const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const cat_mirage *ref_mir = dynamic_cast(ref); const cat_inode *ref_ino = dynamic_cast(ref); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(ref_mir != nullptr) ref_ino = ref_mir->get_inode(); if(ref_ino != nullptr) { if(ref_ino->ea_get_saved_status() == ea_saved_status::full) { if(where->esc == nullptr) throw SRC_BUG; else { where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_ea); } } // else, nothing to do. } // else, nothing to do. } void escape_catalogue::pre_add_crc(const cat_entree *ref, const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const cat_mirage *ref_mir = dynamic_cast(ref); const cat_file *ref_file = dynamic_cast(ref); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(ref_mir != nullptr) ref_file = dynamic_cast(ref_mir->get_inode()); if(ref_file != nullptr) { if(ref_file->get_saved_status() == saved_status::saved || ref_file->get_saved_status() == saved_status::delta) { const crc * c = nullptr; if(ref_file->get_crc(c)) { if(where->esc == nullptr) throw SRC_BUG; where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_file_crc); c->dump(*(where->esc)); } // else, the data may come from an old archive format } // else, nothing to do. } } void escape_catalogue::pre_add_dirty(const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(where->esc == nullptr) throw SRC_BUG; where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_dirty); } void escape_catalogue::pre_add_ea_crc(const cat_entree *ref, const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const cat_mirage *ref_mir = dynamic_cast(ref); const cat_inode *ref_ino = dynamic_cast(ref); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(ref_mir != nullptr) ref_ino = ref_mir->get_inode(); if(ref_ino != nullptr) { if(ref_ino->ea_get_saved_status() == ea_saved_status::full) { const crc * c = nullptr; ref_ino->ea_get_crc(c); if(where->esc == nullptr) throw SRC_BUG; where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_ea_crc); c->dump(*(where->esc)); } // else, nothing to do. } // else, nothing to do. } void escape_catalogue::pre_add_waste_mark(const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(where->esc == nullptr) throw SRC_BUG; where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_changed); } void escape_catalogue::pre_add_failed_mark(const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(where->esc == nullptr) throw SRC_BUG; where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_failed_backup); } void escape_catalogue::pre_add_fsa(const cat_entree *ref, const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const cat_mirage *ref_mir = dynamic_cast(ref); const cat_inode *ref_ino = dynamic_cast(ref); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(ref_mir != nullptr) ref_ino = ref_mir->get_inode(); if(ref_ino != nullptr) { if(ref_ino->fsa_get_saved_status() == fsa_saved_status::full) { if(where->esc == nullptr) throw SRC_BUG; else { where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_fsa); } } // else, nothing to do. } // else, nothing to do. } void escape_catalogue::pre_add_fsa_crc(const cat_entree *ref, const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const cat_mirage *ref_mir = dynamic_cast(ref); const cat_inode *ref_ino = dynamic_cast(ref); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(ref_mir != nullptr) ref_ino = ref_mir->get_inode(); if(ref_ino != nullptr) { if(ref_ino->fsa_get_saved_status() == fsa_saved_status::full) { const crc * c = nullptr; ref_ino->fsa_get_crc(c); if(where->esc == nullptr) throw SRC_BUG; where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_fsa_crc); c->dump(*(where->esc)); } // else, nothing to do. } // else, nothing to do. } void escape_catalogue::pre_add_delta_sig(const pile_descriptor* dest) const { escape_catalogue *ceci = const_cast(this); const pile_descriptor *where = (dest != nullptr) ? dest : &(*(ceci->pdesc)); // pdesc is a smart_pointer not a normal pointer if(where == nullptr) throw SRC_BUG; if(where->esc == nullptr) throw SRC_BUG; where->stack->sync_write_above(where->esc); where->esc->add_mark_at_current_position(escape::seqt_delta_sig); } void escape_catalogue::reset_read() const { escape_catalogue *ceci = const_cast(this); ceci->reset_reading_process(); catalogue::reset_read(); } void escape_catalogue::end_read() const { escape_catalogue *ceci = const_cast(this); ceci->reset_reading_process(); catalogue::end_read(); } void escape_catalogue::skip_read_to_parent_dir() const { escape_catalogue *ceci = const_cast(this); switch(status) { case ec_init: case ec_eod: case ec_detruits: if(cat_det == nullptr) throw SRC_BUG; cat_det->skip_read_to_parent_dir(); break; case ec_marks: ceci->wait_parent_depth = depth; break; case ec_completed: catalogue::skip_read_to_parent_dir(); break; default: throw SRC_BUG; } } bool escape_catalogue::read(const cat_entree * & ref) const { escape_catalogue *ceci = const_cast(this); const cat_directory *ref_dir = nullptr; const cat_eod *ref_eod = nullptr; bool stop = false; // if we have already read the whole archive contents (included detruits object), // we do not need inspect the archive again, we instead use the data in memory if(status == ec_completed) return catalogue::read(ref); if(pdesc.is_null()) throw SRC_BUG; if(pdesc->esc == nullptr) throw SRC_BUG; ref = nullptr; pdesc->stack->flush_read_above(pdesc->esc); try { list cat_signatories; bool only_detruit = false; bool compare_content = false; while(ref == nullptr && !stop) { switch(status) { case ec_init: ceci->status = ec_marks; // no break; case ec_marks: if(min_read_offset > pdesc->esc->get_position()) { // for some reason, like datacorruption, bad CRC or missing CRC in hard linked inode // we skipped back to the hard linked inode information (it failed to be restored the first time) // We must not read again and again the same data, so we skip forward to min_read_offset ceci->pdesc->esc->skip(min_read_offset); } if(pdesc->esc->skip_to_next_mark(escape::seqt_file, true)) { ceci->min_read_offset = pdesc->esc->get_position(); try { ref = cat_entree::read(get_pointer(), pdesc, x_ver.get_edition(), ceci->access_stats(), ceci->corres, x_ver.get_compression_algo(), false, // lax mode false, // only_detruit true); // always a small read in that context if(pdesc->esc->next_to_read_is_mark(escape::seqt_failed_backup)) { if(!pdesc->esc->skip_to_next_mark(escape::seqt_failed_backup, false)) throw SRC_BUG; if(ref != nullptr) { delete ref; ref = nullptr; } continue; // restarts the while loop } ref_dir = dynamic_cast(ref); if(ref_dir != nullptr) ++(ceci->depth); ref_eod = dynamic_cast(ref); if(ref_eod != nullptr) { if(!depth.is_zero()) --(ceci->depth); else if(!x_lax) throw Erange("escape_catalogue::read", gettext("Escape sequences used for reading lead the archive to place some files out of the specified root. To overcome this problem, try reading the archive in direct mode (not using sequential reading), try repairing the archive using Parchive if redundancy data has been created or in last resort try using the lax mode")); else // lax mode { get_ui().message(gettext("LAX MODE: Archive directory structure is corrupted, it would lead to place some files out of the specified root directory. Restoring different directory contents at the root not out of it, which will put files of different directories in the specified root directory")); if(ref == nullptr) throw SRC_BUG; delete ref; // this is the CAT_EOD object ref = nullptr; continue; // restarts the while loop } } } catch(Erange & e) { if(!x_lax) throw; else { get_ui().message(gettext("LAX MODE: found unknown catalogue entry, assuming data corruption occurred. Skipping to the next entry, this may lead to improper directory structure being restored, if the corrupted data was a directory")); ref = nullptr; continue; // restarts the while loop } } if(ref == nullptr) throw Erange("escape_catalogue::read", gettext("Corrupted entry following an escape mark in the archive")); else { bool is_eod = ref_eod != nullptr; cat_entree *ref_nc = const_cast(ref); ceci->add(ref_nc); if(!wait_parent_depth.is_zero()) // we must not return this object as it is member of a skipped dir { if(depth < wait_parent_depth) // we are back out of the skipped directory { if(is_eod) ceci->wait_parent_depth = 0; else throw SRC_BUG; // we should get out of the directory reading a CAT_EOD ! } ref = nullptr; // must not release object except, they are now part of catalogue continue; // ignore this entry and skip to the next one } if(is_eod) ref = get_r_eod_address(); } } else // no more file to read from in-sequence marks { if(!depth.is_zero()) { get_ui().message(gettext("Uncompleted archive! Assuming it has been interrupted during the backup process. If an error has been reported just above, simply ignore it, this is about the file that was saved at the time of the interruption.")); ceci->status = ec_eod; ref = get_r_eod_address(); if(ref == nullptr) throw SRC_BUG; --(ceci->depth); } else { // we no more need the hard link correspondance map so we free the memory it uses ceci->corres.clear(); ceci->status = ec_detruits; label tmp; tmp.clear(); if(pdesc->compr == nullptr) throw SRC_BUG; if(pdesc->compr->is_compression_suspended()) { pdesc->compr->resume_compression(); if(pdesc->compr->get_algo() != compression::none) pdesc->stack->flush_read_above(pdesc->compr); } if(pdesc->esc->skip_to_next_mark(escape::seqt_catalogue, true)) { ceci->status = ec_signature; stop = false; ref = nullptr; } else // no more file and no catalogue entry found (interrupted archive) { ceci->status = ec_completed; if(!x_lax) throw Erange("escape_catalogue::read", gettext("Cannot extract from the internal catalogue the list of files to remove")); else { get_ui().message("LAX MODE: Cannot extract from the internal catalogue the list of files to remove, skipping this step"); ref = nullptr; stop = true; } } } } break; case ec_eod: if(!depth.is_zero()) { ref = get_r_eod_address(); if(ref == nullptr) throw SRC_BUG; --(ceci->depth); } else ceci->status = ec_marks; break; case ec_signature: // build the catalogue to get detruit objects // but first checking the catalogue signature if any // and comparing the internal catalogue to inline catalogue content // we only read detruit objects for internal catalogue // if we have sequentially read data only_detruit = !is_empty(); // we will compare content if encryption has been used and some data // has been read sequentially compare_content = x_ver.is_signed() && only_detruit; // if the archive is not signed and this is not a isolated catalogue // (that's to say we have sequentially read some entries) we can filter // out from the catalogue all non detruits objects. // Else we need the whole catalogue, if it is an isolated catalogue // We need it but temporarily full if the archive is signed to compare // the inernal catalogue content and CRC (which is signed) with inline content // and CRC used so far for sequential reading, then if OK, we must drop from it // all the non detruits objects ceci->cat_det = macro_tools_read_catalogue(get_pointer(), x_ver, *pdesc, 0, // cat_size cannot be determined in sequential_read mode cat_signatories, x_lax, label_zero, only_detruit && ! compare_content); if(ceci->cat_det == nullptr) throw Ememory("escape_catalogue::read"); try { if(!same_signatories(known_sig, cat_signatories)) { string msg = gettext("Archive internal catalogue is not identically signed as the archive itself, this might be the sign the archive has been compromised"); if(x_lax) get_ui().pause(msg); else throw Edata(msg); } if(compare_content) { // checking that all entries read so far // have an identical entry in the internal (signed) // catalogue ceci->status = ec_completed; // necessary to compare the inline content if(!is_subset_of(*cat_det)) { string msg = gettext("Archive internal catalogue is properly signed but its content does not match the tape marks used so far for sequentially reading. Possible data corruption or archive compromission occurred! if data extracted in sequential read mode does not match the data extracted in direct access mode, consider the sequential data has been been modified after the archive has been generated"); if(x_lax) get_ui().pause(msg); else throw Edata(msg); } // filter out all except detruits and directories objects cat_det->drop_all_non_detruits(); } cat_det->reset_read(); if(only_detruit) // some data could be read sequentially { ceci->status = ec_detruits; stop = false; ref = nullptr; } else // not only_detruit and thus not compare_content (isolated catalogue read) { ceci->status = ec_completed; ceci->swap_stuff(*(ceci->cat_det)); delete ceci->cat_det; ceci->cat_det = nullptr; } } catch(...) { if(ceci->cat_det != nullptr) { delete ceci->cat_det; ceci->cat_det = nullptr; } throw; } break; case ec_detruits: if(cat_det == nullptr) throw SRC_BUG; // cat_det has been set to only read detruit() objects // if it fails reading this means we have reached // the end of the detruits if(!cat_det->read(ref)) { ceci->merge_cat_det(); ceci->status = ec_completed; ref = nullptr; stop = true; } else // we return the provided detruit object if(ref == nullptr) throw SRC_BUG; break; case ec_completed: return catalogue::read(ref); default: throw SRC_BUG; } } } catch(...) { if(ref != nullptr && cat_det == nullptr && ref != get_r_eod_address()) // we must not delete objects owned by cat_det delete ref; ref = nullptr; throw; } return ref != nullptr; } bool escape_catalogue::read_if_present(std::string *name, const cat_nomme * & ref) const { escape_catalogue *ceci = const_cast(this); ceci->reset_reading_process(); return catalogue::read_if_present(name, ref); } void escape_catalogue::tail_catalogue_to_current_read() { reset_reading_process(); catalogue::tail_catalogue_to_current_read(); } void escape_catalogue::set_in_place(const path & arg) { string path_s(arg.display()); catalogue::set_in_place(arg); if(status != ec_completed) throw SRC_BUG; // the in_place info should be drop at the // very begining of the archive // writing down the in_place path pdesc->esc->add_mark_at_current_position(escape::seqt_in_place); tools_write_string(*(pdesc->esc), path_s); } void escape_catalogue::clear_in_place() { catalogue::clear_in_place(); if(status != ec_completed) throw SRC_BUG; // the in_place info should be drop at the // very begining of the archive // writing down the absence of in_place path pdesc->esc->add_mark_at_current_position(escape::seqt_in_place); tools_write_string(*(pdesc->esc), "."); } void escape_catalogue::set_esc_and_stack(const pile_descriptor & x_pdesc) { x_pdesc.check(true); // always expecting an escape layer pdesc.assign(new (nothrow) pile_descriptor(x_pdesc)); if(pdesc.is_null()) throw Ememory("escape_catalogue::set_esc_and_stack"); } void escape_catalogue::copy_from(const escape_catalogue & ref) { pdesc = ref.pdesc; x_ver = ref.x_ver; known_sig = ref.known_sig; x_lax = ref.x_lax; corres = ref.corres; status = ref.status; if(ref.cat_det == nullptr) cat_det = nullptr; else cat_det = new (nothrow) catalogue(*ref.cat_det); if(cat_det == nullptr) throw Ememory("escape_catalogue::copy_from"); min_read_offset = ref.min_read_offset; depth = ref.depth; wait_parent_depth = ref.wait_parent_depth; } void escape_catalogue::destroy() { if(cat_det != nullptr) { delete cat_det; cat_det = nullptr; } } void escape_catalogue::merge_cat_det() { if(cat_det != nullptr) { copy_detruits_from(*cat_det); delete cat_det; cat_det = nullptr; } } void escape_catalogue::reset_reading_process() { switch(status) { case ec_init: break; case ec_marks: case ec_eod: get_ui().message(gettext("Resetting the sequential reading process of the archive contents while it is not finished, will make all data unread so far becoming inaccessible")); corres.clear(); status = ec_completed; break; case ec_detruits: merge_cat_det(); status = ec_completed; break; case ec_signature: case ec_completed: break; default: throw SRC_BUG; } depth = 0; wait_parent_depth = 0; } } // end of namespace dar-2.7.15/src/libdar/zapette.cpp0000644000175000017500000002265714636066467013537 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif } // end extern "C" #include #include #include "zapette.hpp" #include "infinint.hpp" #include "tools.hpp" #include "trivial_sar.hpp" #include "sar.hpp" #include "zapette_protocol.hpp" using namespace std; namespace libdar { zapette::zapette(const shared_ptr & dialog, generic_file *input, generic_file *output, bool by_the_end) : generic_file(gf_read_only), mem_ui(dialog) { if(input == nullptr) throw SRC_BUG; if(output == nullptr) throw SRC_BUG; if(input->get_mode() == gf_write_only) throw Erange("zapette::zapette", gettext("Cannot read on input")); if(output->get_mode() == gf_read_only) throw Erange("zapette::zapette", gettext("Cannot write on output")); in = input; out = output; position = 0; serial_counter = 0; contextual::set_info_status(CONTEXT_INIT); ////////////////////////////// // retreiving the file size // S_I tmp = 0; make_transfert(REQUEST_SIZE_SPECIAL_ORDER, REQUEST_OFFSET_GET_FILESIZE, nullptr, "", tmp, file_size); ////////////////////////////// // positionning cursor for next read // depending on the by_the_end value // try { if(by_the_end) { try { skip_to_eof(); } catch(Erange & e) { string tmp = e.get_message(); get_ui().printf(gettext("Failed driving dar_slave to the end of archive: %S. Trying to open the archive from the first bytes"), &tmp); skip(0); } } else skip(0); } catch(...) { terminate(); throw; } } zapette::~zapette() { try { terminate(); } catch(...) { // ignore all exceptions } delete in; delete out; } void zapette::inherited_terminate() { S_I tmp = 0; make_transfert(REQUEST_SIZE_SPECIAL_ORDER, REQUEST_OFFSET_END_TRANSMIT, nullptr, "", tmp, file_size); } bool zapette::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; if(pos >= file_size) { position = file_size; return false; } else { position = pos; return true; } } bool zapette::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; if(x >= 0) { position += x; if(position > file_size) { position = file_size; return false; } else return true; } else if(infinint(-x) > position) { position = 0; return false; } else { position -= (-x); // implicit conversion of "-x" to infinint (positive) return true; } } void zapette::set_info_status(const std::string & s) { infinint val; S_I tmp = 0; if(is_terminated()) throw SRC_BUG; make_transfert(REQUEST_SIZE_SPECIAL_ORDER, REQUEST_OFFSET_CHANGE_CONTEXT_STATUS, nullptr, s, tmp, val); contextual::set_info_status(s); } bool zapette::is_an_old_start_end_archive() const { infinint val; S_I tmp = 0; if(is_terminated()) throw SRC_BUG; make_transfert(REQUEST_SIZE_SPECIAL_ORDER, REQUEST_IS_OLD_START_END_ARCHIVE, nullptr, "", tmp, val); return val == 1; } const label & zapette::get_data_name() const { static label data_name; infinint arg; S_I lu = data_name.size(); // used to specify the amount of space allocated for the answer if(is_terminated()) throw SRC_BUG; make_transfert(REQUEST_SIZE_SPECIAL_ORDER, REQUEST_GET_DATA_NAME, data_name.data(), "", lu, arg); if(lu != (S_I)data_name.size()) throw Erange("zapette::get_data_name", gettext("Uncomplete answer received from peer")); return data_name; } infinint zapette::get_first_slice_header_size() const { infinint ret; S_I tmp; if(is_terminated()) throw SRC_BUG; make_transfert(REQUEST_SIZE_SPECIAL_ORDER, REQUEST_FIRST_SLICE_HEADER_SIZE, nullptr, "", tmp, ret); return ret; } infinint zapette::get_non_first_slice_header_size() const { infinint ret; S_I tmp = 0; if(is_terminated()) throw SRC_BUG; make_transfert(REQUEST_SIZE_SPECIAL_ORDER, REQUEST_OTHER_SLICE_HEADER_SIZE, nullptr, "", tmp, ret); return ret; } U_I zapette::inherited_read(char *a, U_I size) { static const U_16 max_short = ~0; U_I lu = 0; if(size > 0) { infinint not_used; U_16 pas; S_I ret; do { if(size - lu > max_short) pas = max_short; else pas = size - lu; make_transfert(pas, position, a+lu, "", ret, not_used); position += ret; lu += ret; } while(lu < size && ret != 0); } return lu; } void zapette::inherited_write(const char *a, U_I size) { throw SRC_BUG; // zapette is read-only } void zapette::make_transfert(U_16 size, const infinint &offset, char *data, const string & info, S_I & lu, infinint & arg) const { request req; answer ans; // building the request req.serial_num = const_cast(serial_counter)++; // may loopback to 0 req.offset = offset; req.size = size; req.info = info; req.write(out); if(req.size == REQUEST_SIZE_SPECIAL_ORDER) size = lu; // reading the answer do { ans.read(in, data, size); if(ans.serial_num != req.serial_num) get_ui().pause(gettext("Communication problem with peer, retry ?")); } while(ans.serial_num != req.serial_num); // argument affectation switch(ans.type) { case ANSWER_TYPE_DATA: lu = ans.size; arg = 0; break; case ANSWER_TYPE_INFININT: lu = 0; arg = ans.arg; break; default: // might be a transmission error do to weak transport layer throw Erange("zapette::make_transfert", gettext("Incoherent answer from peer")); } // sanity checks if(req.size == REQUEST_SIZE_SPECIAL_ORDER) { if(req.offset == REQUEST_OFFSET_END_TRANSMIT) { if(ans.size != 0 && ans.type != ANSWER_TYPE_DATA) get_ui().message(gettext("Bad answer from peer, while closing connection")); } else if(req.offset == REQUEST_OFFSET_GET_FILESIZE) { if(ans.size != 0 && ans.type != ANSWER_TYPE_INFININT) throw Erange("zapette::make_transfert", gettext("Incoherent answer from peer")); } else if(req.offset == REQUEST_OFFSET_CHANGE_CONTEXT_STATUS) { if(ans.arg != 1) throw Erange("zapette::make_transfert", gettext("Unexpected answer from slave, communication problem or bug may hang the operation")); } else if(req.offset == REQUEST_IS_OLD_START_END_ARCHIVE) { if(ans.type != ANSWER_TYPE_INFININT || (ans.arg != 0 && ans.arg != 1) ) throw Erange("zapetee::make_transfert", gettext("Unexpected answer from slave, communication problem or bug may hang the operation")); } else if(req.offset == REQUEST_GET_DATA_NAME) { if(ans.type != ANSWER_TYPE_DATA && lu != (S_I)(label::common_size())) throw Erange("zapetee::make_transfert", gettext("Unexpected answer from slave, communication problem or bug may hang the operation")); } else if(req.offset == REQUEST_FIRST_SLICE_HEADER_SIZE) { if(ans.size != 0 && ans.type != ANSWER_TYPE_INFININT) throw Erange("zapette::make_transfert", gettext("Incoherent answer from peer")); } else if(req.offset == REQUEST_OTHER_SLICE_HEADER_SIZE) { if(ans.size != 0 && ans.type != ANSWER_TYPE_INFININT) throw Erange("zapette::make_transfert", gettext("Incoherent answer from peer")); } else throw Erange("zapette::make_transfert", gettext("Corrupted data read from pipe")); } } } // end of namespace dar-2.7.15/src/libdar/crc.hpp0000644000175000017500000001225414636066467012627 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file crc.hpp /// \brief class crc definition, used to handle Cyclic Redundancy Checks /// \ingroup Private #ifndef CRC_HPP #define CRC_HPP #include "../my_config.h" #include #include #include "integers.hpp" #include "storage.hpp" #include "infinint.hpp" #include "proto_generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// pure virtual class defining interface of a CRC object class crc { public: static constexpr U_I OLD_CRC_SIZE = 2; crc() {}; crc(const crc & ref) = default; crc(crc && ref) noexcept = default; crc & operator = (const crc & ref) = default; crc & operator = (crc && ref) noexcept = default; virtual ~crc() = default; virtual bool operator == (const crc & ref) const = 0; bool operator != (const crc & ref) const { return ! (*this == ref); }; virtual void compute(const infinint & offset, const char *buffer, U_I length) = 0; virtual void compute(const char *buffer, U_I length) = 0; // for sequential read only virtual void clear() = 0; virtual void dump(proto_generic_file & f) const = 0; virtual std::string crc2str() const = 0; virtual infinint get_size() const = 0; virtual crc *clone() const = 0; }; /// generate a CRC object reading it from file extern crc *create_crc_from_file(proto_generic_file & f, bool old = false); /// generate a CRC object with adhoc width based on a file size extern crc *create_crc_from_size(infinint width); /// crc implementation based on infinint class crc_i : public crc { public: crc_i(const infinint & width); crc_i(const infinint & width, proto_generic_file & f); crc_i(const crc_i & ref) : size(ref.size), cyclic(ref.size) { copy_data_from(ref); pointer = cyclic.begin(); }; crc_i(crc_i && ref) noexcept = default; crc_i & operator = (const crc_i & ref) { copy_from(ref); return *this; }; crc_i & operator = (crc_i && ref) noexcept = default; ~crc_i() = default; bool operator == (const crc & ref) const override; virtual void compute(const infinint & offset, const char *buffer, U_I length) override; virtual void compute(const char *buffer, U_I length) override; // for sequential read only virtual void clear() override; virtual void dump(proto_generic_file & f) const override; virtual std::string crc2str() const override; virtual infinint get_size() const override { return size; }; protected: virtual crc *clone() const override { crc *tmp = new (std::nothrow) crc_i(*this); if(tmp == nullptr) throw Ememory("crc"); return tmp; }; private: infinint size; ///< size of the checksum storage::iterator pointer; ///< points to the next byte to modify storage cyclic; ///< the checksum storage void copy_from(const crc_i & ref); void copy_data_from(const crc_i & ref); }; /// crc implementation based on U_I class crc_n : public crc { public: crc_n(U_I width); crc_n(U_I width, proto_generic_file & f); crc_n(const crc_n & ref) { copy_from(ref); }; crc_n(crc_n && ref) noexcept = default; crc_n & operator = (const crc_n & ref); crc_n & operator = (crc_n && ref) noexcept = default; ~crc_n() { destroy(); }; bool operator == (const crc & ref) const override; virtual void compute(const infinint & offset, const char *buffer, U_I length) override; virtual void compute(const char *buffer, U_I length) override; // for sequential read only virtual void clear() override; virtual void dump(proto_generic_file & f) const override; virtual std::string crc2str() const override; virtual infinint get_size() const override { return size; }; protected: virtual crc *clone() const override { crc *tmp = new (std::nothrow) crc_n(*this); if(tmp == nullptr) throw Ememory("crc"); return tmp; }; private: U_I size; ///< size of checksum (non infinint mode) unsigned char *pointer; ///< points to the next byte to modify (non infinint mode) unsigned char *cyclic; ///< the checksum storage (non infinint mode) void alloc(U_I width); void copy_from(const crc_n & ref); void copy_data_from(const crc_n & ref); void destroy(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/gf_mode.hpp0000644000175000017500000000321114636066467013451 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file gf_mode.hpp /// \brief generic modes to open file /// \ingroup Private /// \note API included module due to dependencies #ifndef GF_MODE_HPP #define GF_MODE_HPP #include "../my_config.h" extern "C" { } // end extern "C" namespace libdar { /// \addtogroup Private /// @{ /// generic_file openning modes enum gf_mode { gf_read_only, ///< read only access gf_write_only, ///< write only access gf_read_write ///< read and write access }; /// provides a human readable string defining the gf_mode given in argument extern const char * generic_file_get_name(gf_mode mode); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/entrepot.cpp0000644000175000017500000001031314636066467013705 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include extern "C" { #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "entrepot.hpp" #include "cygwin_adapt.hpp" #include "tools.hpp" #include "hash_fichier.hpp" #include "cache_global.hpp" #include "tuyau_global.hpp" using namespace std; namespace libdar { entrepot::entrepot(): where("/"), root("/") { user = ""; group = ""; } void entrepot::set_location(const path & chemin) { if(where != chemin) { read_dir_flush(); where = chemin; } } void entrepot::set_root(const path & p_root) { if(p_root.is_relative()) throw Erange("entrepot::set_root", std::string(gettext("root's entrepot must be an absolute path: ")) + p_root.display()); root = p_root; } path entrepot::get_full_path() const { if(get_location().is_relative()) return get_root() + get_location(); else return get_location(); } fichier_global *entrepot::open(const shared_ptr & dialog, const std::string & filename, gf_mode mode, bool force_permission, U_I permission, bool fail_if_exists, bool erase, hash_algo algo, bool provide_a_plain_file) const { fichier_global *ret = nullptr; tuyau_global *pipe_g = nullptr; // sanity check if(algo != hash_algo::none && (mode != gf_write_only || (!erase && !fail_if_exists))) throw SRC_BUG; // if hashing is asked, we cannot accept to open an existing file without erasing its contents try { // creating the file to read from or write to data ret = inherited_open(dialog, filename, mode, force_permission, permission, fail_if_exists, erase); if(ret == nullptr) throw SRC_BUG; if(!provide_a_plain_file) { pipe_g = new (nothrow) tuyau_global(dialog, ret); if(pipe_g == nullptr) throw Ememory("entrepot::open"); else ret = nullptr; // now managed by pipe_g ret = pipe_g; pipe_g = nullptr; } if(algo != hash_algo::none) { fichier_global *hash_file = nullptr; fichier_global *tmp = nullptr; // creating the file to write hash to try { hash_file = inherited_open(dialog, filename+"."+hash_algo_to_string(algo), gf_write_only, force_permission, permission, fail_if_exists, erase); if(hash_file == nullptr) throw SRC_BUG; try { tmp = new (nothrow) hash_fichier(dialog, ret, filename, hash_file, algo); if(tmp == nullptr) throw Ememory("entrepot::entrepot"); else { ret = tmp; hash_file = nullptr; } } catch(...) { if(hash_file != nullptr) delete hash_file; throw; } } catch(Egeneric & e) { e.prepend_message(gettext("Error met while creating the hash file: ")); throw; } } } catch(...) { if(ret != nullptr) { delete ret; ret = nullptr; } if(pipe_g != nullptr) { delete pipe_g; pipe_g = nullptr; } throw; } return ret; } } // end of namespace dar-2.7.15/src/libdar/user_group_bases.cpp0000644000175000017500000000625014636066467015421 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #ifdef __DYNAMIC__ extern "C" { #if HAVE_SYS_TYPE_H #include #endif #if HAVE_PWD_H #include #endif #if HAVE_GRP_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif } #include "erreurs.hpp" #include "user_group_bases.hpp" #include "tools.hpp" #include "erreurs.hpp" using namespace std; #if MUTEX_WORKS #define CRITICAL_START \ sigset_t Critical_section_mask_memory; \ tools_block_all_signals(Critical_section_mask_memory); \ pthread_mutex_lock(&lock_fill) #define CRITICAL_END pthread_mutex_unlock(&lock_fill); \ tools_set_back_blocked_signals(Critical_section_mask_memory) #else #define CRITICAL_START // not a thread-safe implementation #define CRITICAL_END // not a thread-safe implementation #endif namespace libdar { const std::string user_group_bases::empty_string = ""; #if MUTEX_WORKS pthread_mutex_t user_group_bases::lock_fill = PTHREAD_MUTEX_INITIALIZER; #endif void user_group_bases::fill() const { if(!filled) { user_group_bases *me = const_cast(this); struct passwd *pwd; struct group *grp; CRITICAL_START; // filling the user name base if(me == nullptr) throw SRC_BUG; setpwent(); // reset password reading while((pwd = getpwent()) != nullptr) me->user_database[pwd->pw_uid] = pwd->pw_name; endpwent(); // filling the group name base setgrent(); while((grp = getgrent()) != nullptr) me->group_database[grp->gr_gid] = grp->gr_name; endgrent(); CRITICAL_END; me->filled = true; } } const string & user_group_bases::get_username(const infinint & uid) const { map::const_iterator it; fill(); it = user_database.find(uid); if(it != user_database.end()) return it->second; else return empty_string; } const string & user_group_bases::get_groupname(const infinint & gid) const { map::const_iterator it; fill(); it = group_database.find(gid); if(it != group_database.end()) return it->second; else return empty_string; } } // end of namespace #endif dar-2.7.15/src/libdar/i_database.cpp0000644000175000017500000005163214636066467014132 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if STDC_HEADERS #include #endif #if HAVE_ERRNO_H #include #endif } // end extern "C" #include #include #include #include "tools.hpp" #include "storage.hpp" #include "nls_swap.hpp" #include "database_header.hpp" #include "i_archive.hpp" #include "i_database.hpp" using namespace libdar; using namespace std; static storage *file2storage(generic_file &f); static void memory2file(storage &s, generic_file &f); namespace libdar { database::i_database::i_database(const shared_ptr & dialog): mem_ui(dialog) { archive_data dat; dat.chemin = ""; dat.basename = ""; coordinate.clear(); coordinate.push_back(dat); // coordinate[0] is never used, but must exist options_to_dar.clear(); dar_path = ""; files = new (nothrow) data_dir("."); // "." or whaterver else (this name is not used) if(files == nullptr) throw Ememory("database::i_database::database"); data_files = nullptr; check_order_asked = true; cur_db_version = database_header_get_supported_version(); algo = compression::gzip; // stays the default algorithm for new databases compr_level = 9; // stays the default compression level for new databases } database::i_database::i_database(const shared_ptr & dialog, const string & base, const database_open_options & opt): mem_ui(dialog) { generic_file *f = database_header_open(dialog, base, cur_db_version, algo, compr_level); if(f == nullptr) throw Ememory("database::i_database::database"); try { check_order_asked = opt.get_warn_order(); build(*f, opt.get_partial(), opt.get_partial_read_only(), cur_db_version); } catch(...) { delete f; throw; } delete f; } void database::i_database::build(generic_file & f, bool partial, bool read_only, const unsigned char db_version) { NLS_SWAP_IN; try { struct archive_data dat; if(db_version > database_header_get_supported_version()) throw SRC_BUG; // we should not get there if the database is more recent than what that software can handle. this is necessary if we do not want to destroy the database or loose data. coordinate.clear(); infinint tmp = infinint(f); // number of archive to read while(!tmp.is_zero()) { tools_read_string(f, dat.chemin); tools_read_string(f, dat.basename); if(db_version >= 3) dat.root_last_mod.read(f, db2archive_version(db_version)); else dat.root_last_mod = datetime(0); coordinate.push_back(dat); --tmp; } if(coordinate.empty()) throw Erange("database::i_database::database", gettext("Badly formatted database")); tools_read_vector(f, options_to_dar); tools_read_string(f, dar_path); if(db_version < database_header_get_supported_version()) partial = false; if(!partial) { files = data_dir::data_tree_read(f, db_version); if(files == nullptr) throw Ememory("database::i_database::database"); if(files->get_name() != ".") files->set_name("."); data_files = nullptr; } else { if(!read_only) { files = nullptr; data_files = file2storage(f); } else { files = nullptr; data_files = nullptr; } } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } database::i_database::~i_database() { if(files != nullptr) delete files; if(data_files != nullptr) delete data_files; } void database::i_database::dump(const std::string & filename, const database_dump_options & opt) const { if(files == nullptr && data_files == nullptr) throw Erange("database::i_database::dump", gettext("Cannot write down a read-only database")); generic_file *f = database_header_create(get_pointer(), filename, opt.get_overwrite(), algo, compr_level); if(f == nullptr) throw Ememory("database::i_database::dump"); try { archive_num tmp = coordinate.size(); infinint(tmp).dump(*f); for(archive_num i = 0; i < tmp; ++i) { tools_write_string(*f, coordinate[i].chemin); tools_write_string(*f, coordinate[i].basename); coordinate[i].root_last_mod.dump(*f); } tools_write_vector(*f, options_to_dar); tools_write_string(*f, dar_path); if(files != nullptr) files->dump(*f); else if(data_files != nullptr) memory2file(*data_files, *f); else throw SRC_BUG; } catch(...) { if(f != nullptr) delete f; throw; } if(f != nullptr) delete f; } void database::i_database::add_archive(const archive & arch, const string & chemin, const string & basename, const database_add_options & opt) { // note: this methode could now leverage the use of // list_entry provided by op_listing to a callback function, but // it does not worth the effort as it brings no value to the code // and would probably cost the introduction of some bugs // // so we need accessing the internal catalogue object of a given archive // to keep this implementation as is NLS_SWAP_IN; try { struct archive_data dat; archive_num number = coordinate.size(); if(files == nullptr) throw SRC_BUG; if(basename == "") throw Erange("database::i_database::add_archive", gettext("Empty string is an invalid archive basename")); dat.chemin = chemin; dat.basename = basename; dat.root_last_mod = arch.pimpl->get_catalogue().get_root_dir_last_modif(); coordinate.push_back(dat); files->data_tree_update_with(arch.pimpl->get_catalogue().get_contenu(), number); if(number > 1) files->finalize_except_self(number, get_root_last_mod(number), 0); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::i_database::remove_archive(archive_num min, archive_num max, const database_remove_options & opt) { NLS_SWAP_IN; try { min = get_real_archive_num(min, opt.get_revert_archive_numbering()); max = get_real_archive_num(max, opt.get_revert_archive_numbering()); if(min > max) throw Erange("database::i_database::remove_archive", gettext("Incorrect archive range in database")); if(min == 0 || max >= coordinate.size()) throw Erange("database::i_database::remove_archive", gettext("Incorrect archive range in database")); for(U_I i = max ; i >= min ; --i) { if(files == nullptr) throw SRC_BUG; files->remove_all_from(i, coordinate.size() - 1); files->skip_out(i); coordinate.erase(coordinate.begin() + i); } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::i_database::change_name(archive_num num, const string & basename, const database_change_basename_options &opt) { NLS_SWAP_IN; try { num = get_real_archive_num(num, opt.get_revert_archive_numbering()); if(num < coordinate.size() && num != 0) coordinate[num].basename = basename; else throw Erange("database::i_database::change_name", gettext("Non existent archive in database")); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::i_database::set_path(archive_num num, const string & chemin, const database_change_path_options & opt) { NLS_SWAP_IN; try { num = get_real_archive_num(num, opt.get_revert_archive_numbering()); if(num < coordinate.size() && coordinate[num].basename != "") coordinate[num].chemin = chemin; else throw Erange("database::i_database::change_name", gettext("Non existent archive in database")); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::i_database::set_permutation(archive_num src, archive_num dst) { NLS_SWAP_IN; try { struct archive_data moved; if(files == nullptr) throw SRC_BUG; if(src >= coordinate.size() || src <= 0) throw Erange("database::i_database::set_permutation", string(gettext("Invalid archive number: ")) + tools_int2str(src)); if(dst >= coordinate.size() || dst <= 0) throw Erange("database::i_database::set_permutation", string(gettext("Invalid archive number: ")) + tools_int2str(dst)); moved = coordinate[src]; coordinate.erase(coordinate.begin()+src); coordinate.insert(coordinate.begin()+dst, moved); files->apply_permutation(src, dst); // update et_absent dates set re_finalize; set::iterator re_it; if(src < dst) { re_finalize.insert(src); re_finalize.insert(dst); if(dst+1 < (archive_num)coordinate.size()) re_finalize.insert(dst+1); } else // src >= dst { if(src+1 < (archive_num)coordinate.size()) re_finalize.insert(src+1); re_finalize.insert(dst); if(dst+1 < (archive_num)coordinate.size()) re_finalize.insert(dst+1); // if src == dst the set still contains on entry (src or dst). // this is intended to let the user have the possibility // to ask dates recompilation, even if in theory this is useless } re_it = re_finalize.begin(); while(re_it != re_finalize.end()) { files->finalize_except_self(*re_it, get_root_last_mod(*re_it), *re_it + 1); ++re_it; } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } database_archives_list database::i_database::get_contents() const { database_archives_list ret; database_archives tmp; ret.push_back(tmp); // index 0 is not used for(archive_num i = 1; i < coordinate.size(); ++i) { tmp.set_path(coordinate[i].chemin); tmp.set_basename(coordinate[i].basename); ret.push_back(tmp); } return ret; } void database::i_database::get_files(database_listing_show_files_callback callback, void *context, archive_num num, const database_used_options & opt) const { NLS_SWAP_IN; try { if(num != 0) num = get_real_archive_num(num, opt.get_revert_archive_numbering()); if(files == nullptr) throw SRC_BUG; if(num < coordinate.size()) files->show(callback, context, num); else throw Erange("database::i_database::show_files", gettext("Non existent archive in database")); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::i_database::get_version(database_listing_get_version_callback callback, void *context, path chemin) const { NLS_SWAP_IN; try { const data_tree *ptr = nullptr; const data_dir *ptr_dir = files; string tmp; if(files == nullptr) throw SRC_BUG; if(!chemin.is_relative()) throw Erange("database::i_database::show_version", gettext("Invalid path, path must be relative")); while(chemin.pop_front(tmp) && ptr_dir != nullptr) { ptr = ptr_dir->read_child(tmp); if(ptr == nullptr) throw Erange("database::i_database::show_version", gettext("Non existent file in database")); ptr_dir = dynamic_cast(ptr); } if(ptr_dir == nullptr) throw Erange("database::i_database::show_version", gettext("Non existent file in database")); ptr = ptr_dir->read_child(chemin.display()); if(ptr == nullptr) throw Erange("database::i_database::show_version", gettext("Non existent file in database")); else ptr->listing(callback, context); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::i_database::show_most_recent_stats(database_listing_statistics_callback callback, void *context) const { NLS_SWAP_IN; try { deque stats_data(coordinate.size(), 0); deque stats_ea(coordinate.size(), 0); deque total_data(coordinate.size(), 0); deque total_ea(coordinate.size(), 0); if(files == nullptr) throw SRC_BUG; if(callback == nullptr) throw Erange("database::i_database::show_most_recent_stats", "nullptr provided as user callback function"); files->compute_most_recent_stats(stats_data, stats_ea, total_data, total_ea); try { for(archive_num i = 1; i < coordinate.size(); ++i) callback(context, i, stats_data[i], total_data[i], stats_ea[i], total_ea[i]); } catch(...) { throw Elibcall("database::i_database::show_most_recent_stats", "user provided callback should not throw any exception"); } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::i_database::restore(const vector & filename, const database_restore_options & opt) { NLS_SWAP_IN; try { map > command_line; deque anneau; const data_tree *ptr; anneau.assign(filename.begin(), filename.end()); if(files == nullptr) throw SRC_BUG; if(opt.get_info_details()) get_ui().message(gettext("Checking chronological ordering of files between the archives...")); check_order(); // determination of the archives to restore and files to restore for each selected file while(!anneau.empty()) { if(files == nullptr) throw SRC_BUG; if(files->data_tree_find(anneau.front(), ptr)) { const data_dir *ptr_dir = dynamic_cast(ptr); set num_data; archive_num max_data = 0; archive_num num_ea = 0; db_lookup look_ea, look_data; look_data = ptr->get_data(num_data, opt.get_date(), opt.get_even_when_removed()); look_ea = ptr->get_EA(num_ea, opt.get_date(), opt.get_even_when_removed()); switch(look_data) { case db_lookup::found_present: break; case db_lookup::found_removed: num_data.clear(); // we do not restore any data if(opt.get_info_details()) get_ui().message(string(gettext("File recorded as removed at this date in database: ")) + anneau.front()); break; case db_lookup::not_found: num_data.clear(); get_ui().message(string(gettext("File not found in database: ")) + anneau.front()); break; case db_lookup::not_restorable: num_data.clear(); get_ui().message(string(gettext("File found in database but impossible to restore (only found \"unchanged\" in differential backups, or delta patch without reference to base it on in any previous archive of the base): ")) + anneau.front()); break; default: throw SRC_BUG; } switch(look_ea) { case db_lookup::found_present: num_ea = 0; // we cannot restore it break; case db_lookup::found_removed: num_ea = 0; // we cannot restore it break; case db_lookup::not_found: num_ea = 0; // we cannot restore it break; case db_lookup::not_restorable: num_ea = 0; // we cannot restore it get_ui().message(string(gettext("Extended Attribute of file found in database but impossible to restore (only found \"unchanged\" in differential backups): ")) + anneau.front()); break; default: throw SRC_BUG; } // if no data nor EA could be found if(num_ea == 0 && num_data.empty()) { if(!opt.get_date().is_zero()) // a date was specified { string fic = anneau.front(); if(opt.get_info_details()) get_ui().printf(gettext("%S did not exist before specified date and cannot be restored"), &fic); } } else // there is something to restore for that file { // if latest EA are not to be found in a archive where // data has also to be restored from, adding a specific command-line for EA if(num_ea != 0 && num_data.find(num_ea) != num_data.end()) { command_line[num_ea].push_back("-g"); command_line[num_ea].push_back(anneau.front()); } // adding command-line for archives where to find data (and possibly EA) for(set::iterator it = num_data.begin(); it != num_data.end(); ++it) { command_line[*it].push_back("-g"); command_line[*it].push_back(anneau.front()); if(*it > max_data) max_data = *it; } // warning if latest EA is not found with the same archive as the most recent Data if(max_data != 0 && num_ea != 0) if(max_data > num_ea) // will restore "EA only" then "data + old EA" { string fic = anneau.front(); if(!opt.get_even_when_removed()) get_ui().printf(gettext("Either archives in database are not properly tidied, or file last modification date has been artificially set to an more ancient date. This may lead improper Extended Attribute restoration for inode %S"), &fic); } } if(ptr_dir != nullptr) { // adding current directory children in the list of files vector fils; vector::iterator fit; path base = anneau.front(); ptr_dir->read_all_children(fils); fit = fils.begin(); while(fit != fils.end()) anneau.push_back((base.append(*(fit++))).display()); } } else { string fic = anneau.front(); get_ui().printf(gettext("Cannot restore file %S : non existent file in database"), &fic); } anneau.pop_front(); } //freeing memory if early_release is set if(opt.get_early_release()) { if(files != nullptr) { delete files; files = nullptr; } } // calling dar for each archive if(!command_line.empty()) { string dar_cmd = dar_path != "" ? dar_path : "dar"; map >::iterator ut = command_line.begin(); vector argvector_init = vector(1, dar_cmd); while(ut != command_line.end()) { try { string archive_name; vector argvpipe; // building the argument list sent through anonymous pipe if(coordinate[ut->first].chemin != "") archive_name = coordinate[ut->first].chemin + "/"; else archive_name = ""; archive_name += coordinate[ut->first].basename; argvpipe.push_back(dar_cmd); // just to fill the argv[0] by the command even when transmitted through anonymous pipe argvpipe.push_back("-x"); argvpipe.push_back(archive_name); if(!opt.get_ignore_dar_options_in_database()) argvpipe += options_to_dar; argvpipe += opt.get_extra_options_for_dar(); argvpipe += ut->second; get_ui().printf("CALLING DAR: restoring %d files from archive %S using anonymous pipe to transmit configuration to the dar process", ut->second.size()/2, &archive_name); if(opt.get_info_details()) { get_ui().printf("Arguments sent through anonymous pipe are:"); get_ui().message(tools_concat_vector(" ", argvpipe)); } tools_system_with_pipe(get_pointer(), dar_cmd, argvpipe); } catch(Erange & e) { get_ui().message(string(gettext("Error while restoring the following files: ")) + tools_concat_vector( " ", ut->second) + " : " + e.get_message()); } ut++; } } else get_ui().message(gettext("Cannot restore any file, nothing done")); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } archive_num database::i_database::get_real_archive_num(archive_num num, bool revert) const { if(num == 0) throw Erange("database::i_database::get_real_archive_num", tools_printf(dar_gettext("Invalid archive number: %d"), num)); if(revert) { U_I size = coordinate.size(); // size is +1 because of record zero that is never used but must exist if(size > num) return size - num; else throw Erange("database::i_database::get_real_archive_num", tools_printf(dar_gettext("Invalid archive number: %d"), -num)); } else return num; } const datetime & database::i_database::get_root_last_mod(const archive_num & num) const { if(num >= coordinate.size()) throw SRC_BUG; return coordinate[num].root_last_mod; } } // end of namespace static storage *file2storage(generic_file &f) { storage *st = new (nothrow) storage(0); const U_I taille = 102400; unsigned char buffer[taille]; S_I lu; if(st == nullptr) throw Ememory("dar_manager:file2storage"); do { lu = f.read((char *)buffer, taille); if(lu > 0) st->insert_bytes_at_iterator(st->end(), buffer, lu); } while(lu > 0); return st; } static void memory2file(storage &s, generic_file &f) { s.dump(f); } dar-2.7.15/src/libdar/gzip_module.cpp0000644000175000017500000001113514636066467014366 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_ZLIB_H #include #endif } #include "gzip_module.hpp" #include "int_tools.hpp" #include "tools.hpp" using namespace std; namespace libdar { gzip_module::gzip_module(U_I compression_level) { #if LIBZ_AVAILABLE if(compression_level > 9 || compression_level < 1) throw Erange("gzip_module::gzip_module", tools_printf(gettext("out of range GZIP compression level: %d"), compression_level)); level = compression_level; #else throw Ecompilation(gettext("gzip compression")); #endif } U_I gzip_module::get_max_compressing_size() const { #if LIBZ_AVAILABLE U_I unused = 0; return int_tools_maxof_aggregate(unused); #else throw Ecompilation(gettext("gzip compression")); #endif } U_I gzip_module::get_min_size_to_compress(U_I clear_size) const { #if LIBZ_AVAILABLE if(clear_size > get_max_compressing_size() || clear_size < 1) throw Erange("gzip_module::get_min_size_to_compress", "out of range block size submitted to gzip_module::get_min_size_to_compress"); return compressBound(clear_size); #else throw Ecompilation(gettext("gzip compression")); #endif } U_I gzip_module::compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const { #if LIBZ_AVAILABLE S_I ret; uLong zip_buf_size_ulong = zip_buf_size; if(normal_size > get_max_compressing_size()) throw Erange("gzip_module::compress_data", "oversized uncompressed data given to GZIP compression engine"); ret = compress2((Bytef*)zip_buf, &zip_buf_size_ulong, (const Bytef*)normal, normal_size, level); zip_buf_size = (U_I)(zip_buf_size_ulong); if((uLong)zip_buf_size != zip_buf_size_ulong) throw SRC_BUG; // integer overflow occured switch(ret) { case Z_OK: break; case Z_MEM_ERROR: throw Erange("gzip_module::compress_data", "lack of memory to perform the gzip compression operation"); case Z_BUF_ERROR: throw Erange("gzip_module::compress_data", "too small buffer provided to receive compressed data"); case Z_STREAM_ERROR: throw Erange("gzip_module::compress_data", gettext("invalid compression level provided to the gzip compression engine")); default: throw SRC_BUG; } return zip_buf_size; // compress2 modified zip_buf_size to give the resulting amount of compressed data #else throw Ecompilation(gettext("gzip compression")); #endif } U_I gzip_module::uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const { #if LIBZ_AVAILABLE uLongf normal_size_ulong = normal_size; S_I ret = uncompress((Bytef*)normal, &normal_size_ulong, (const Bytef*)zip_buf, zip_buf_size); normal_size = normal_size_ulong; if((uLongf)(normal_size) != normal_size_ulong) throw SRC_BUG; // integer overflow occured switch(ret) { case Z_OK: break; case Z_MEM_ERROR: throw Erange("gzip_module::uncompress_data", "lack of memory to perform the gzip decompression operation"); case Z_BUF_ERROR: throw Erange("gzip_module::uncompress_data", "too small buffer provided to receive decompressed data"); case Z_DATA_ERROR: throw Edata(gettext("corrupted compressed data met")); default: throw SRC_BUG; } return normal_size; #else throw Ecompilation(gettext("gzip compression")); #endif } unique_ptr gzip_module::clone() const { #if LIBZ_AVAILABLE try { return std::make_unique(*this); } catch(bad_alloc &) { throw Ememory("gzip_module::clone"); } #else throw Ecompilation(gettext("gzip compression")); #endif } } // end of namespace dar-2.7.15/src/libdar/fichier_local.cpp0000644000175000017500000003360614636066467014642 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" #include "infinint.hpp" #include "generic_file.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "cygwin_adapt.hpp" #include "int_tools.hpp" #include "tools.hpp" #include "fichier_local.hpp" #include "user_interaction_blind.hpp" #include #include #define BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFFER_SIZE #undef BUFFER_SIZE #define BUFFER_SIZE SSIZE_MAX #endif #endif using namespace std; namespace libdar { fichier_local::fichier_local(const shared_ptr & dialog, const string & chemin, gf_mode m, U_I permission, bool fail_if_exists, bool erase, bool furtive_mode) : fichier_global(dialog, m) { fichier_local::open(chemin, m, permission, fail_if_exists, erase, furtive_mode); } fichier_local::fichier_local(const string & chemin, bool furtive_mode) : fichier_global(shared_ptr(new (nothrow) user_interaction_blind()), gf_read_only) { // in read-only mode the user_interaction is not expected to be used fichier_local::open(chemin, gf_read_only, 0, false, false, furtive_mode); } void fichier_local::change_ownership(const std::string & user, const std::string & group) { if(is_terminated()) throw SRC_BUG; // this method cannot be inlined to avoid cyclic dependency in headers files // fichier_global.hpp would then needs tools.hpp, which need limitint.hpp which relies // back on fichier.hpp tools_set_ownership(filedesc, user, group); } void fichier_local::change_permission(U_I perm) { if(is_terminated()) throw SRC_BUG; // this method cannot be inlined to avoid cyclic dependency in headers files // fichier_global.hpp would then needs tools.hpp, which need limitint.hpp which relies // back on fichier.hpp tools_set_permission(filedesc, perm); } infinint fichier_local::get_size() const { struct stat dat; infinint filesize; if(is_terminated()) throw SRC_BUG; if(filedesc < 0) throw SRC_BUG; if(fstat(filedesc, &dat) < 0) throw Erange("fichier_local::get_size()", string(gettext("Error getting size of file: ")) + tools_strerror_r(errno)); else filesize = dat.st_size; return filesize; } void fichier_local::fadvise(advise adv) const { if(is_terminated()) throw SRC_BUG; #if HAVE_POSIX_FADVISE int ret = posix_fadvise(filedesc, 0, 0, advise_to_int(adv)); if(ret == EBADF) throw SRC_BUG; // filedesc not a valid file descriptor !?! if(ret != 0) throw Erange("fichier_local::fadvise", string("Set posix advise failed: ") + tools_strerror_r(errno)); #endif } void fichier_local::fsync() const { if(is_terminated()) throw SRC_BUG; #if HAVE_FDATASYNC S_I st = ::fdatasync(filedesc); #else S_I st = ::fsync(filedesc); #endif if(st < 0) throw Erange("fichier_local::fsync", string("Failed sync the slice (fdatasync): ") + tools_strerror_r(errno)); } bool fichier_local::skip(const infinint &q) { off_t delta; infinint pos = q; if(is_terminated()) throw SRC_BUG; if(lseek(filedesc, 0, SEEK_SET) < 0) return false; do { delta = 0; pos.unstack(delta); if(delta > 0) if(lseek(filedesc, delta, SEEK_CUR) < 0) return false; } while(delta > 0); return true; } bool fichier_local::skip_to_eof() { if(is_terminated()) throw SRC_BUG; return lseek(filedesc, 0, SEEK_END) >= 0; } bool fichier_local::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; if(x > 0) { if(lseek(filedesc, x, SEEK_CUR) < 0) return false; else return true; } if(x < 0) { bool ret = true; off_t actu = lseek(filedesc, 0, SEEK_CUR); if(actu < -x) { actu = 0; ret = false; } else actu += x; // x is negative if(lseek(filedesc, actu, SEEK_SET) < 0) ret = false; return ret; } return true; } infinint fichier_local::get_position() const { if(is_terminated()) throw SRC_BUG; off_t ret = lseek(filedesc, 0, SEEK_CUR); if(ret == -1) throw Erange("fichier_local::get_position", string(gettext("Error getting file reading position: ")) + tools_strerror_r(errno)); return ret; } void fichier_local::inherited_truncate(const infinint & pos) { off_t offset = 0; int ret; infinint tmp_pos = pos; if(is_terminated()) throw SRC_BUG; tmp_pos.unstack(offset); if(!tmp_pos.is_zero()) throw Erange("fichier_local::inherited_truncate", gettext("File too large for the operating system to be truncate at the requested position")); if(offset >= get_eof_offset()) return; // will not expand the file size ret = ftruncate(filedesc, offset); if(ret != 0) throw Erange("fichier_local::inherited_truncate", string(dar_gettext("Error while calling system call truncate(): ")) + tools_strerror_r(errno)); if(get_position() > pos) skip_to_eof(); } bool fichier_local::fichier_global_inherited_read(char *a, U_I size, U_I & read, string & message) { ssize_t ret = -1; read = 0; #ifdef MUTEX_WORKS check_self_cancellation(); #endif do { #ifdef SSIZE_MAX U_I to_read = size - read > SSIZE_MAX ? SSIZE_MAX : size - read; #else U_I to_read = size - read; #endif ret = ::read(filedesc, a+read, to_read); if(ret < 0) { switch(errno) { case EINTR: break; case EAGAIN: throw SRC_BUG; // "non blocking" read is not expected in this implementation case EIO: throw Ehardware("fichier_local::inherited_read", string(gettext("Error while reading from file: ")) + tools_strerror_r(errno)); default : throw Erange("fichier_local::inherited_read", string(gettext("Error while reading from file: ")) + tools_strerror_r(errno)); } } else read += ret; } while(read < size && ret != 0); if(adv == advise_dontneed) fadvise(adv); return true; // we never make partial reading, here } U_I fichier_local::fichier_global_inherited_write(const char *a, U_I size) { ssize_t ret; U_I total = 0; // due to posix inconsistence in write(2) // we cannot write more than SSIZE_MAX/2 byte // to be able to have a positive returned value // in case of success, a negative else. As the // returned type of write() is signed size_t, // which max positive value is SSIZE_MAX/2 (rounded // to the lower interger) #ifdef SSIZE_MAX static const U_I step = SSIZE_MAX/2; #else const U_I step = size; // which is no limit... #endif #ifdef MUTEX_WORKS check_self_cancellation(); #endif while(total < size) { if(size - total > step) ret = ::write(filedesc, a+total, step); else ret = ::write(filedesc, a+total, size - total); if(ret < 0) { switch(errno) { case EINTR: break; case EIO: throw Ehardware("fichier_local::inherited_write", string(gettext("Error while writing to file: ")) + tools_strerror_r(errno)); case ENOSPC: return total; // partial writing, we stop here returning the amount of data wrote so far // because there is no space left on device. The parent class manages the user interaction // to allow abortion or action that frees up some storage space. default : throw Erange("fichier_local::inherited_write", string(gettext("Error while writing to file: ")) + tools_strerror_r(errno)); } } else total += ret; } if(adv == advise_dontneed) // we should call fsync() here but we do not to to avoid blocking the process: // we assume the system has flushed some blocks of possibly previous writes // so we just inform the system to remove from cache blocks associated to this // file that have already been flushed. fadvise(adv); return total; } void fichier_local::open(const string & chemin, gf_mode m, U_I permission, bool fail_if_exists, bool erase, bool furtive_mode) { U_I o_mode = O_BINARY; const char *name = chemin.c_str(); adv = advise_normal; switch(m) { case gf_read_only : o_mode |= O_RDONLY; break; case gf_write_only : o_mode |= O_WRONLY; break; case gf_read_write : o_mode |= O_RDWR; break; default: throw SRC_BUG; } if(m != gf_read_only) { o_mode |= O_CREAT; if(fail_if_exists) o_mode |= O_EXCL; if(erase) o_mode |= O_TRUNC; } #if FURTIVE_READ_MODE_AVAILABLE if(furtive_mode) // only used for read-only, but available for write-only and read-write modes o_mode |= O_NOATIME; #else if(furtive_mode) throw Ecompilation(gettext("Furtive read mode")); #endif try { do { if(m != gf_read_only) filedesc = ::open(name, o_mode, permission); else filedesc = ::open(name, o_mode); if(filedesc < 0) { switch(errno) { case ENOSPC: if(get_mode() == gf_read_only) throw SRC_BUG; // in read_only mode we do not need to create a new inode !!! get_ui().pause(gettext("No space left for inode, you have the opportunity to make some room now. When done : can we continue ?")); break; case EEXIST: throw Esystem("fichier_local::open", tools_strerror_r(errno), Esystem::io_exist); case ENOENT: throw Esystem("fichier_local::open", tools_strerror_r(errno), Esystem::io_absent); case EACCES: throw Esystem("fichier_local::open", tools_strerror_r(errno), Esystem::io_access); case EROFS: throw Esystem("fichier_local::open", tools_strerror_r(errno), Esystem::io_ro_fs); default: throw Erange("fichier_local::open", string(gettext("Cannot open file : ")) + tools_strerror_r(errno)); } } } while(filedesc < 0 && errno == ENOSPC); } catch(...) { if(filedesc >= 0) { ::close(filedesc); filedesc = -1; } throw; } } void fichier_local::copy_from(const fichier_local & ref) { filedesc = dup(ref.filedesc); if(filedesc < 0) { string tmp = tools_strerror_r(errno); throw Erange("fichier_local::copy_from", tools_printf(gettext("Cannot dup() filedescriptor while copying \"fichier_local\" object: %s"), tmp.c_str())); } adv = ref.adv; } void fichier_local::move_from(fichier_local && ref) noexcept { swap(filedesc, ref.filedesc); swap(adv, ref.adv); } int fichier_local::advise_to_int(advise arg) const { #if HAVE_POSIX_FADVISE switch(arg) { case advise_normal: return POSIX_FADV_NORMAL; case advise_sequential: return POSIX_FADV_SEQUENTIAL; case advise_random: return POSIX_FADV_RANDOM; case advise_noreuse: return POSIX_FADV_NOREUSE; case advise_willneed: return POSIX_FADV_WILLNEED; case advise_dontneed: return POSIX_FADV_DONTNEED; default: throw SRC_BUG; } #else return 0; #endif } off_t fichier_local::get_eof_offset() const { off_t ret; off_t tmp; off_t cur = lseek(filedesc, 0, SEEK_CUR); if(cur < 0) throw Erange("fichier_local::get_eof_offset()", string("Error while reading current file offset: ") + tools_strerror_r(errno)); ret = lseek(filedesc, 0, SEEK_END); if(ret < 0) throw Erange("fichier_local::get_eof_offset()", string("Error while reading current file offset: ") + tools_strerror_r(errno)); tmp = lseek(filedesc, cur, SEEK_SET); if(tmp < 0) throw Erange("fichier_local::get_eof_offset()", string("Error while seeking back to previous offset: ") + tools_strerror_r(errno)); if(tmp != cur) throw SRC_BUG; return ret; } } // end of namespace dar-2.7.15/src/libdar/cat_chardev.cpp0000644000175000017500000000260114636066467014311 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_chardev.hpp" using namespace std; namespace libdar { bool cat_chardev::operator == (const cat_entree & ref) const { const cat_chardev *ref_chardev = dynamic_cast(&ref); if(ref_chardev == nullptr) return false; else return cat_device::operator == (ref); } } // end of namespace dar-2.7.15/src/libdar/sparse_file.cpp0000644000175000017500000003042114636066467014343 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" #include "sparse_file.hpp" #define BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFFER_SIZE #undef BUFFER_SIZE #define BUFFER_SIZE SSIZE_MAX #endif #endif #include "crc.hpp" using namespace std; namespace libdar { // sparse_file class static members: bool sparse_file::initialized = false; unsigned char sparse_file::zeroed_field[SPARSE_FIXED_ZEROED_BLOCK]; // sparse_file::sparse_file(generic_file *below, const infinint & hole_size) : escape(below, std::set()), zero_count(0), offset(0) { // change the escape sequences fixed part to not collide with a possible underlying (the "below" object or below this "below" object) escape object change_fixed_escape_sequence(ESCAPE_FIXED_SEQUENCE_SPARSE_FILE); if(!initialized) { // we avoid the cost of using a mutex, as even if two or more threads execute this statement at the same time, // the first to finish will avoid other new thread to change these values executing this code, // while the thread already executing this code, will just redo the same thing as what has been done by the first thread to finish // thread (zero the area to zeros). (void)memset(zeroed_field, 0, SPARSE_FIXED_ZEROED_BLOCK); initialized = true; } reset(); copy_to_no_skip = false; if(below == nullptr) throw SRC_BUG; min_hole_size = hole_size; UI_min_hole_size = 0; min_hole_size.unstack(UI_min_hole_size); if(!min_hole_size.is_zero()) // hole size is larger than maximum buffer UI_min_hole_size = 0; // disabling hole lookup inside buffers (faster execution) min_hole_size = hole_size; // setting back min_hole_size to its value } infinint sparse_file::get_position() const { if(is_terminated()) throw SRC_BUG; switch(get_mode()) { case gf_read_only: if(zero_count > offset) throw SRC_BUG; return offset - zero_count; case gf_write_only: return offset + zero_count; default: throw SRC_BUG; } } U_I sparse_file::inherited_read(char *a, U_I size) { U_I lu = 0; bool eof = false; U_I tmp; U_I needed; if(escape_read) return escape::inherited_read(a, size); while(lu < size && ! eof) { needed = size - lu; switch(mode) { case hole: if(zero_count.is_zero()) // start of a new hole { if(!next_to_read_is_mark(seqt_file)) { sequence_type t; if(next_to_read_is_which_mark(t)) if(t == seqt_file) throw SRC_BUG; else throw Erange("sparse_file::inherited_read", gettext("Incoherent structure in data carrying sparse files: unknown mark")); // we were not at the end of file, while an escape sequence different from seqt_file was met else eof = true; // no mark next to be read, thus we are at EOF for real } else if(skip_to_next_mark(seqt_file, false)) { read_as_escape(true); try { try { zero_count.read(*this); } catch(Egeneric &e) { e.prepend_message("Error while reading the size of a hole:"); throw; } } catch(...) { read_as_escape(false); throw; } read_as_escape(false); seen_hole = true; offset += zero_count; } else throw SRC_BUG; // the next to read mark was seqt_file, but could not skip forward to that mark !?! } else // zero_count > 0 , whe have not yet read all the bytes of the hole { U_I available = 0; zero_count.unstack(available); if(available == 0) throw SRC_BUG; // could not unstack, but zero_count not equal to zero !?! else { if(needed < available) { (void)memset(a + lu, 0, needed); zero_count += available - needed; lu += needed; } else { (void)memset(a + lu, 0, available); lu += available; // zero_count is already null, due to previous call to unstack() } } if(zero_count.is_zero()) mode = normal; } break; case normal: tmp = escape::inherited_read(a + lu, needed); if(has_escaped_data_since_last_skip()) data_escaped = true; offset += tmp; lu += tmp; if(tmp < needed) { zero_count = 0; mode = hole; } break; default: throw SRC_BUG; } } return lu; } void sparse_file::copy_to(generic_file &ref, const infinint & crc_size, crc * & value) { char buffer[BUFFER_SIZE]; S_I lu; bool loop = true; bool last_is_skip = false; if(is_terminated()) throw SRC_BUG; if(!crc_size.is_zero()) { value = create_crc_from_size(crc_size); if(value == nullptr) throw SRC_BUG; } else value = nullptr; try { do { lu = escape::inherited_read(buffer, BUFFER_SIZE); if(has_escaped_data_since_last_skip()) data_escaped = true; if(lu > 0) { if(!crc_size.is_zero()) value->compute(offset, buffer, lu); ref.write(buffer, lu); offset += lu; last_is_skip = false; } else // lu == 0 if(next_to_read_is_mark(seqt_file)) { if(!skip_to_next_mark(seqt_file, false)) throw SRC_BUG; else { read_as_escape(true); try { zero_count.read(*this); } catch(...) { read_as_escape(false); zero_count = 0; throw; } read_as_escape(false); if(copy_to_no_skip) { while(!zero_count.is_zero()) { U_I to_write = 0; zero_count.unstack(to_write); while(to_write > 0) { U_I min = to_write > SPARSE_FIXED_ZEROED_BLOCK ? SPARSE_FIXED_ZEROED_BLOCK : to_write; ref.write((const char *)zeroed_field, min); to_write -= min; } } } else // using skip to restore hole into the copied-to generic_file { offset += zero_count; zero_count = 0; if(!ref.skip(offset)) throw Erange("sparse_file::copy_to", gettext("Cannot skip forward to restore a hole")); last_is_skip = true; seen_hole = true; } } } else // reached EOF ? { sequence_type m; if(next_to_read_is_which_mark(m)) // this is not EOF, but unsued mark is present if(m == seqt_file) throw SRC_BUG; // should have been reported above by next_to_read_is_mark(seqt_file) else throw Erange("sparse_file::copy", gettext("Data corruption or unknown sparse_file mark found in file's data")); else // Yes, this is the EOF { if(last_is_skip) { (void)ref.skip_relative(-1); ref.write((const char *)&zeroed_field, 1); } loop = false; } } } while(loop); } catch(...) { if(value != nullptr) { delete value; value = nullptr; } throw; } } void sparse_file::inherited_write(const char *a, U_I size) { U_I written = 0; U_I hole_start = 0; U_I hole_length = 0; if(is_terminated()) throw SRC_BUG; if(escape_write) return escape::inherited_write(a, size); while(written < size) { switch(mode) { case normal: if(look_for_hole(a + written, size - written, UI_min_hole_size, hole_start, hole_length)) { U_I next_data = written + hole_start + hole_length; if(hole_length < UI_min_hole_size) throw SRC_BUG; // bug in look for hole! escape::inherited_write(a + written, hole_start); if(has_escaped_data_since_last_skip()) data_escaped = true; if(next_data < size) // hole is inside "a" { write_hole(hole_length); written = next_data; } else // hole is at the end of "a" { mode = hole; zero_count = hole_length; offset += written + hole_start; // offset points at the start of the hole written = size; // this ends the while loop } } else // no hole in the remaing data to inspect (either hole size is larger than any possible buffer or no hole could be found in buffer) { escape::inherited_write(a + written, size - written); offset += size; written = size; // this ends the while loop if(has_escaped_data_since_last_skip()) data_escaped = true; } break; case hole: if(written > 0) throw SRC_BUG; // we should not pass from normal to hole inside a single call to this same method written = count_initial_zeros(a, size); if(written < size) // some normal data are present after the hole { zero_count += written; dump_pending_zeros(); offset -= written; // to have offset giving the offset of the first byte of "a" // instead of the first byte after the written hole (which is shifted in "a" // by an amount of "written" byte(s). } else // all data of the buffer is part of the current hole zero_count += size; break; default: throw SRC_BUG; } } } void sparse_file::inherited_sync_write() { switch(mode) { case hole: dump_pending_zeros(); break; case normal: break; default: throw SRC_BUG; } escape::inherited_sync_write(); } void sparse_file::dump_pending_zeros() { if(mode != hole) throw SRC_BUG; offset += zero_count; if(zero_count <= min_hole_size) { U_I tmp = 0; // write down zeroed bytes normally (zero_count bytes) do { zero_count.unstack(tmp); while(tmp > 0) { if(tmp > SPARSE_FIXED_ZEROED_BLOCK) { escape::inherited_write((const char *)zeroed_field, SPARSE_FIXED_ZEROED_BLOCK); tmp -= SPARSE_FIXED_ZEROED_BLOCK; } else { escape::inherited_write((const char *)zeroed_field, tmp); tmp = 0; } } } while(!zero_count.is_zero()); } else // enough data to record a hole { write_hole(zero_count); } zero_count = 0; mode = normal; } void sparse_file::write_hole(const infinint & length) { add_mark_at_current_position(seqt_file); write_as_escape(true); // to avoid recursion dumping offset try { length.dump(*this); } catch(...) { write_as_escape(false); // back in normal writing mode throw; } write_as_escape(false); // back in normal writing mode seen_hole = true; } void sparse_file::reset() { mode = normal; zero_count = 0; escape_write = false; escape_read = false; seen_hole = false; data_escaped = false; } bool sparse_file::look_for_hole(const char *a, U_I size, U_I min_hole_size, U_I & start, U_I & length) { U_I inspected = 0; length = 0; while(inspected < size) { start = inspected; while(start < size && a[start] != '\0') ++start; if(start < size) { inspected = start + 1; while(inspected < size && a[inspected] == '\0') ++inspected; } else inspected = start; length = inspected - start; if(min_hole_size > 0 && length > min_hole_size) inspected = size; else { ++inspected; length = 0; } } return length > 0; } U_I sparse_file::count_initial_zeros(const char *a, U_I size) { U_I curs = 0; while(curs < size && a[curs] == '\0') ++curs; return curs; } } // end of namespace dar-2.7.15/src/libdar/storage.cpp0000644000175000017500000005402214636066467013516 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif } // end of extern "C" #include "storage.hpp" #include "infinint.hpp" #include "integers.hpp" #include "tools.hpp" using namespace std; namespace libdar { storage::storage(const infinint & size) { make_alloc(size, first, last); } storage::storage(proto_generic_file & f, const infinint & size) { U_32 lu, tmp; make_alloc(size, first, last); struct cellule *ptr = first; try { while(ptr != nullptr) { lu = 0; do { tmp = f.read(((char *)(ptr->data))+lu, ptr->size - lu); lu += tmp; } while(lu < ptr->size && tmp != 0); if(lu < ptr->size) throw Erange("storage::storage", gettext("Not enough data to initialize storage field")); ptr = ptr->next; } } catch(...) { detruit(first); first = nullptr; last = nullptr; throw; } } unsigned char storage::operator [](const infinint &position) const { return const_cast(*this)[position]; } unsigned char & storage::operator [](infinint position) { U_32 offset = 0; struct cellule *ptr = first; do { if(ptr == nullptr) throw Erange("storage::operator[]", gettext("Asking for an element out of array")); if(offset > ptr->size) { offset -= ptr->size; ptr = ptr->next; } else position.unstack(offset); } while(offset > ptr->size); return ptr->data[offset]; } infinint storage::size() const noexcept { infinint ret = 0; struct cellule *ptr = first; while(ptr != nullptr) { ret += ptr->size; ptr = ptr->next; } return ret; } void storage::clear(unsigned char val) noexcept { struct cellule *cur = first; while(cur != nullptr) { (void)memset(cur->data, val, cur->size); cur = cur->next; } } void storage::dump(proto_generic_file & f) const { const struct cellule *ptr = first; while(ptr != nullptr) { f.write((const char *)(ptr->data), ptr->size); ptr = ptr->next; } } U_I storage::write(iterator & it, unsigned char *a, U_I size) { if(it.ref != this) throw Erange("storage::write", gettext("The iterator is not indexing the object it has been asked to write to")); U_I wrote = 0; while(wrote < size && it != end()) { U_32 to_write = size - wrote; U_32 space = it.cell->size - it.offset; if(to_write <= space) { // enough room in current data block (void)memcpy(it.cell->data + it.offset, a + wrote, to_write); wrote += to_write; it.offset += to_write; } else { // more to copy than available in current data block (void)memcpy(it.cell->data + it.offset, a + wrote, space); wrote += space; it.cell = it.cell->next; if(it.cell != nullptr) it.offset = 0; else it.offset = iterator::OFF_END; } } return wrote; } U_I storage::read(iterator & it, unsigned char *a, U_I size) const { if(it.ref != this) throw Erange("storage::read", gettext("The iterator is not indexing the object it has been asked to read from")); U_I read = 0; while(read < size && it != end()) { U_32 to_read = size - read; U_32 space = it.cell->size - it.offset; if(to_read <= space) { // enough room in current data block (void)memcpy(a + read, it.cell->data + it.offset, to_read); read += to_read; it.offset += to_read; } else { // more to copy than available in current data block (void)memcpy(a + read, it.cell->data + it.offset, space); read += space; it.cell = it.cell->next; if(it.cell != nullptr) it.offset = 0; else it.offset = iterator::OFF_END; } } return read; } void storage::insert_null_bytes_at_iterator(iterator it, U_I size) { unsigned char a = 0; insert_bytes_at_iterator_cmn(it, true, &a, size); } void storage::insert_const_bytes_at_iterator(iterator it, unsigned char a, U_I size) { insert_bytes_at_iterator_cmn(it, true, &a, size); } void storage::insert_bytes_at_iterator(iterator it, unsigned char *a, U_I size) { insert_bytes_at_iterator_cmn(it, false, a, size); } void storage::insert_as_much_as_necessary_const_byte_to_be_as_wider_as(const storage & ref, const iterator &it, unsigned char value) { S_32 to_add = 0; const cellule *c_ref = ref.first; cellule *c_me = first; while((c_ref != nullptr || to_add > 0) && (c_me != nullptr || to_add <= 0)) { if(to_add > 0) { to_add -= c_me->size; c_me = c_me->next; } else { to_add += c_ref->size; c_ref = c_ref->next; } } while(to_add > 0) { insert_const_bytes_at_iterator(it, value, to_add); if(c_ref != nullptr) { to_add = c_ref->size; c_ref = c_ref->next; } else to_add = 0; } } void storage::remove_bytes_at_iterator(iterator it, U_I number) { while(number > 0 && it.cell != nullptr) { U_I can_rem = it.cell->size - it.offset; if(can_rem < number) { if(it.offset > 0) // we must keep data of the current cellule located before it.offset { unsigned char *p = nullptr; p = new (nothrow) unsigned char[it.offset]; if(p != nullptr) { (void)memcpy(p, it.cell->data, it.offset); delete [] it.cell->data; it.cell->data = p; it.cell->size -= can_rem; it.cell = it.cell->next; it.offset = 0; number -= can_rem; } else throw Ememory("storage::remove_bytes_at_iterator"); } else // we can remove from the chain the whole cellule { struct cellule *t = it.cell->next; if(t != nullptr) it.cell->next->prev = it.cell->prev; else last = it.cell->prev; if(it.cell->prev != nullptr) it.cell->prev->next = t; else first = t; number -= it.cell->size; it.cell->next = nullptr; it.cell->prev = nullptr; detruit(it.cell); it.cell = t; } } else // can_rem >= number, some data of the current cell must be kept after part to be removed { unsigned char *p = nullptr; p = new (nothrow) unsigned char[it.cell->size - number]; if(p != nullptr) { (void)memcpy(p, it.cell->data, it.offset); (void)memcpy(p + it.offset, it.cell->data + it.offset + number, it.cell->size - it.offset - number); delete [] it.cell->data; it.cell->data = p; it.cell->size -= number; number = 0; } else throw Ememory("storage::remove_bytes_at_iterator"); } } reduce(); } void storage::remove_bytes_at_iterator(iterator it, infinint number) { U_32 sz = 0; number.unstack(sz); while(sz > 0) { remove_bytes_at_iterator(it, sz); sz = 0; number.unstack(sz); } } void storage::truncate(const infinint & pos) { if(pos < size()) { iterator it; infinint amount = size() - pos; it.skip_to(*this, pos); remove_bytes_at_iterator(it, amount); } } void storage::fusionne(struct cellule *a_first, struct cellule *a_last, struct cellule *b_first, struct cellule *b_last, struct cellule *&res_first, struct cellule * & res_last) { if((a_first == nullptr) ^ (a_last == nullptr)) throw SRC_BUG; if((b_first == nullptr) ^ (b_last == nullptr)) throw SRC_BUG; if(a_last != nullptr && b_first != nullptr) { a_last->next = b_first; b_first->prev = a_last; res_first = a_first; res_last = b_last; } else if(a_first == nullptr) { res_first = b_first; res_last = b_last; } else { res_first = a_first; res_last = a_last; } } void storage::copy_from(const storage & ref) { U_32 pas = 0, delta; struct cellule *ptr = ref.first; first = last = nullptr; try { while(ptr != nullptr || pas > 0) { if(ptr != nullptr) { delta = pas + ptr->size; ptr = ptr->next; } else delta = 0; if(delta < pas) // must make the allocation { struct cellule *debut, *fin; make_alloc(pas, debut, fin); fusionne(first, last, debut, fin, first, last); pas = delta; } else pas = delta; } } catch(Ememory & e) { detruit(first); first = last = nullptr; throw; } iterator i_ref = ref.begin(); iterator i_new = begin(); while(i_ref != ref.end()) { *i_new = *i_ref; ++i_new; ++i_ref; } } void storage::move_from(storage && ref) noexcept { swap(first, ref.first); swap(last, ref.last); } S_32 storage::difference(const storage & ref) const { struct cellule *b = last, *a = ref.last; S_32 superior = 0; while((a != nullptr || superior <= 0) && (b != nullptr || superior >= 0) && (a != nullptr || b != nullptr)) { if(superior >= 0 && a != nullptr) { superior -= a->size; a = a->next; } if(superior <= 0 && b != nullptr) { superior += b->size; b = b->next; } } return superior; } void storage::reduce() { struct cellule *glisseur = first; U_32 failed_alloc = ~0; while(glisseur != nullptr) { if(glisseur->next != nullptr) { U_I somme = glisseur->next->size + glisseur->size; if(somme < failed_alloc) { unsigned char *p = nullptr; p = new (nothrow) unsigned char[somme]; if(p != nullptr) { struct cellule *tmp = glisseur->next; (void)memcpy(p, glisseur->data, glisseur->size); (void)memcpy(p + glisseur->size, tmp->data, somme - glisseur->size); delete [] glisseur->data; glisseur->data = p; glisseur->size = somme; glisseur->next = tmp->next; if(glisseur->next != nullptr) glisseur->next->prev = glisseur; else last = glisseur; tmp->next = tmp->prev = nullptr; detruit(tmp); } else // alloc failed { failed_alloc = somme; glisseur = glisseur->next; } } else // no fusion possible glisseur = glisseur->next; } else // no next cellule glisseur = glisseur->next; } } void storage::insert_bytes_at_iterator_cmn(iterator it, bool constant, unsigned char *a, U_I size) { if(it.ref != this) throw Erange("storage::insert_bytes_at_iterator_cmn", gettext("The iterator is not indexing the object it has been defined for")); if(size == 0) return; // nothing to insert if(it.cell != nullptr) { storage temp = size+it.cell->size; struct cellule *before, *after; iterator gliss = temp.begin(); if(constant) temp.clear(*a); temp.write(gliss, it.cell->data, it.offset); if(!constant) temp.write(gliss, a, size); else gliss += size; temp.write(gliss, it.cell->data+it.offset, it.cell->size-it.offset); if(temp.first == nullptr || temp.last == nullptr) throw SRC_BUG; // now we move the chain of cellule from the temp object into the current object (this) // first we release the cellule that has been copied to "temp" object before = it.cell->prev; after = it.cell->next; it.cell->prev = nullptr; it.cell->next = nullptr; detruit(it.cell); it.cell = nullptr; if(before != nullptr) before->next = temp.first; else first = temp.first; temp.first->prev = before; if(after != nullptr) after->prev = temp.last; else last = temp.last; temp.last->next = after; temp.first = temp.last = nullptr; // this way when "temp" object will be destroyed // it will not affect the chain of cells which is now // part of "this" (current object). } else // it_cell == nullptr { storage temp = size; if(constant) temp.clear(*a); else { iterator ut = temp.begin(); temp.write(ut, a,size); } switch(it.offset) { case iterator::OFF_END : if(last != nullptr) last->next = temp.first; else first = temp.first; if(temp.first == nullptr) throw SRC_BUG; temp.first->prev = last; last = temp.last; break; case iterator::OFF_BEGIN : if(first != nullptr) first->prev = temp.last; else last = temp.last; if(temp.last == nullptr) throw SRC_BUG; temp.last->next = first; first = temp.first; break; default: throw SRC_BUG; } temp.last = temp.first = nullptr; } reduce(); } void storage::detruit(struct cellule *c) { struct cellule *t; while(c != nullptr) { if(c->data != nullptr) { delete [] c->data; c->data = nullptr; } t = c->next; delete c; c = t; // ready to destroy the next cellule } } void storage::make_alloc(U_32 size, struct cellule * & begin, struct cellule * & end) { struct cellule *newone; struct cellule *previous = nullptr; U_32 dsize = size; begin = end = nullptr; if(size > 0) { do { newone = new (nothrow) cellule; if(newone != nullptr) { newone->prev = previous; newone->next = nullptr; if(previous != nullptr) previous->next = newone; else begin = newone; } else { detruit(begin); begin = nullptr; throw Ememory("storage::make_alloc"); } do { newone->data = new (nothrow) unsigned char[dsize]; if(newone->data != nullptr) { size -= dsize; newone->size = dsize; previous = newone; } else if(dsize > 2) dsize /= 2; else { newone->size = 0; detruit(begin); begin = nullptr; throw Ememory("storage::make_alloc"); } } while(dsize > 1 && newone->data == nullptr); } while (size > 0); end = newone; } } void storage::make_alloc(infinint size, struct cellule * & begin, struct cellule * &end) { struct cellule *debut; struct cellule *fin; U_32 sz = 0; begin = end = nullptr; if(!size.is_zero()) { size.unstack(sz); do { try { make_alloc(sz, debut, fin); if(end != nullptr) { end->next = debut; debut->prev = end; end = fin; } else if(begin != nullptr) throw SRC_BUG; else { begin = debut; end = fin; } } catch(Ememory & e) { if(begin != nullptr) { detruit(begin); begin = nullptr; end = nullptr; } throw; } sz = 0; size.unstack(sz); } while(sz > 0); } } /////////////////////////////////////////////////////////// //////////////////////// ITERATOR ///////////////////////// /////////////////////////////////////////////////////////// storage::iterator & storage::iterator::operator += (U_32 s) { S_32 t = s >> 1; S_32 r = s & 0x1; relative_skip_to(t); relative_skip_to(t+r); return *this; } storage::iterator & storage::iterator::operator -= (U_32 s) { static const U_32 max = (U_32)(~0) >> 1; // maximum U_32 that can also be S_32 if(s > max) { S_32 t = s >> 1; // equivalent to s/2; S_32 r = s & 0x01; // equivalent to s%2; relative_skip_to(-t); relative_skip_to(-t); relative_skip_to(-r); } else relative_skip_to(-(S_32)(s)); return *this; } unsigned char & storage::iterator::operator *() const { if(points_on_data()) return cell->data[offset]; else throw Erange("storage::iterator::operator *()", gettext("Iterator does not point to data")); } void storage::iterator::skip_to(const storage & st, infinint val) { U_16 pas = 0; // relative_skip_to has S_32 as argument, cannot call it with U_32 *this = st.begin(); val.unstack(pas); do { relative_skip_to(pas); pas = 0; val.unstack(pas); } while(pas > 0); } void storage::iterator::relative_skip_to(S_32 val) { if(val >= 0) { while(val > 0 && cell != nullptr) { if(offset + val >= cell->size) { val -= cell->size - offset; cell = cell->next; offset = 0; } else { offset += val; val = 0; } } if(cell == nullptr) offset = OFF_END; } else while(val < 0 && cell != nullptr) { val += offset; if(val < 0) { cell = cell->prev; if(cell != nullptr) offset = cell->size; else offset = OFF_BEGIN; } else offset = val; } } infinint storage::iterator::get_position() const { if(ref == nullptr || ref->first == nullptr) throw Erange("storage::iterator::get_position", gettext("Reference storage of the iterator is empty or non existent")); struct cellule *p = ref->first; infinint ret = 0; if(cell == nullptr) throw Erange("storage::iterator::get_position", gettext("Iterator does not point to data")); while(p != nullptr && p != cell) { ret += p->size; p = p->next; } if(p != nullptr) ret += offset; else throw Erange("storage::iterator::get_position", gettext("The iterator position is not inside the storage of reference")); return ret; } } // end of namespace dar-2.7.15/src/libdar/cat_prise.cpp0000644000175000017500000000256414636066467014027 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_prise.hpp" using namespace std; namespace libdar { bool cat_prise::operator == (const cat_entree & ref) const { const cat_prise *ref_prise = dynamic_cast(&ref); if(ref_prise == nullptr) return false; else return cat_inode::operator == (ref); } } // end of namespace dar-2.7.15/src/libdar/cache_global.hpp0000644000175000017500000001100314636067146014425 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cache_global.hpp /// \brief adaptation of the cache class to the fichier_global interface /// \ingroup Private #ifndef CACHE_GLOBAL_HPP #define CACHE_GLOBAL_HPP #include "../my_config.h" #include "cache.hpp" #include "fichier_global.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the cache_global cache is an adaptation of the cache class to the fichier_global interface class cache_global: public fichier_global { public: static const U_I default_cache_size = 102400; /// constructor /// \param[in] dialog for user interaction requested by fichier_global /// \param[in] x_ptr the hidden/underlying generic_file to provide caching feature for /// \param[in] shift_mode see cache class constructor for details /// \param[in] size cache size /// \note the object pointed to by x_ptr passed under the responsibility of the cache_global object, /// it will be automatically deleted when no more needed cache_global(const std::shared_ptr & dialog, fichier_global *x_ptr, bool shift_mode, U_I size = default_cache_size); /// copy constructor cache_global(cache_global & ref) = delete; /// move constructor cache_global(cache_global && ref) = delete; /// assignment operator cache_global & operator = (const cache_global & ref) = delete; /// move assignment operator cache_global & operator = (cache_global && ref) = delete; /// destructor ~cache_global() { detruit(); }; // inherited from fichier_global virtual void change_ownership(const std::string & user, const std::string & group) override { ptr->change_ownership(user, group); }; virtual void change_permission(U_I perm) override { ptr->change_permission(perm); }; virtual infinint get_size() const override { return ptr->get_size(); }; virtual void fadvise(advise adv) const override { ptr->fadvise(adv); }; // inherited from generic_file grand-parent class virtual bool skippable(skippability direction, const infinint & amount) override { return buffer->skippable(direction, amount); }; virtual bool skip(const infinint & pos) override { return buffer->skip(pos); }; virtual bool skip_to_eof() override { return buffer->skip_to_eof(); }; virtual bool skip_relative(S_I x) override { return buffer->skip_relative(x); }; virtual bool truncatable(const infinint & pos) const override { return buffer->truncatable(pos); }; virtual infinint get_position() const override { return buffer->get_position(); }; // expose cache specific methods void change_to_read_write() { buffer->change_to_read_write(); }; protected: // inherited from fichier_global virtual U_I fichier_global_inherited_write(const char *a, U_I size) override { buffer->write(a, size); return size; }; virtual bool fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) override { read = buffer->read(a, size); return true; }; // inherted from generic_file virtual void inherited_read_ahead(const infinint & amount) override { buffer->read_ahead(amount); }; virtual void inherited_truncate(const infinint & pos) override { buffer->truncate(pos); }; virtual void inherited_sync_write() override { buffer->sync_write(); ptr->sync_write(); }; virtual void inherited_flush_read() override { buffer->flush_read(); }; virtual void inherited_terminate() override { buffer->terminate(); ptr->terminate(); }; private: cache *buffer; fichier_global *ptr; void detruit(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/user_interaction.cpp0000644000175000017500000000651014636066467015426 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_FCNTL_H #include #endif #if STDC_HEADERS #include #endif } // end extern "C" #include #include "user_interaction.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { void user_interaction::pause(const string & message) { bool ret = false; try { ret = inherited_pause(message); } catch(...) { throw Elibcall("user_interaction::pause", "user_interaction::inherited_pause should not throw an exception toward libdar"); } if(!ret) throw Euser_abort(message); } void user_interaction::message(const string & message) { try { return inherited_message(message); } catch(...) { throw Elibcall("user_interaction::warning", "user_interaction::inherited_warning should not throw an exception toward libdar"); } } string user_interaction::get_string(const string & message, bool echo) { try { return inherited_get_string(message, echo); } catch(...) { throw Elibcall("user_interaction::get_string", "user_interaction::inherited_get_string should not throw an exception toward libdar"); } } secu_string user_interaction::get_secu_string(const string & message, bool echo) { try { return inherited_get_secu_string(message, echo); } catch(...) { throw Elibcall("user_interaction::get_secu_string", "user_interaction::inherited_get_secu_string should not throw an exception toward libdar"); } } void user_interaction::printf(const char *format, ...) { va_list ap; va_start(ap, format); try { message(tools_vprintf(format, ap)); } catch(...) { va_end(ap); throw; } va_end(ap); } } // end of namespace dar-2.7.15/src/libdar/user_group_bases.hpp0000644000175000017500000000572314636066467015432 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file user_group_bases.hpp /// \brief defines class that speed up the uid to username and gid to group name lookup /// \ingroup Private #ifndef USER_GROUP_BASES_HPP #define USER_GROUP_BASES_HPP #include "../my_config.h" #ifdef __DYNAMIC__ extern "C" { #if MUTEX_WORKS #if HAVE_PTHREAD_H #include #endif #endif } #include #include #include "infinint.hpp" namespace libdar { /// \addtogroup Private /// @{ class user_group_bases { public: user_group_bases() : filled(false) {}; user_group_bases(const user_group_bases & ref) = default; user_group_bases(user_group_bases && ref) noexcept = default; user_group_bases & operator = (const user_group_bases & ref) = default; user_group_bases & operator = (user_group_bases && ref) noexcept = default; ~user_group_bases() = default; /// return the user name corresponding to the given uid /// \note if the entry is not known, returns an empty string const std::string & get_username(const infinint & uid) const; /// return the group name corresponding to the given gid /// \note if the entry is not known, returns an empty string const std::string & get_groupname(const infinint & gid) const; private: bool filled; std::map user_database; std::map group_database; void fill() const; static const std::string empty_string; #if MUTEX_WORKS /// mutex to access the operating system information /// the system call used to read the password and group database are not re-entrant, the mutex /// must block all other thread while a given thread is reading the databases /// the best solution would have to let the user provide such a database object already filled /// but that would rely on it not to destroy this object while threads are using it /// for this reason, here the mutex is 'static' static pthread_mutex_t lock_fill; #endif }; /// @} } // end of namespace #endif #endif dar-2.7.15/src/libdar/mycurl_param_list.hpp0000644000175000017500000001633514636066467015612 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mycurl_param_list.hpp /// \brief wrapper for element a CURL* object can receive as parameter in order to be put in etherogeneous list /// \ingroup Private #ifndef MYCURL_PARAM_LIST_HPP #define MYCURL_PARAM_LIST_HPP #include "../my_config.h" extern "C" { #if LIBCURL_AVAILABLE #if HAVE_CURL_CURL_H #include #endif #endif } // end extern "C" #include #include #include #include #include "integers.hpp" #include "erreurs.hpp" namespace libdar { /// \addtogroup Private /// @{ // libcurl uses a list of options to a CURL handle, which argument is of variable type // we want to record this list to be able to reset, copy, and do other fancy operations // on CURL handle and have all these managed within a single class (mycurl_easyhandle_node) // for that we need here a list of association of CURLoption type with variable typed value // // This is implemented by a ancestor type (mycurl_param_element_generic) which is an pure abstracted // class, from which derives many template based classes: mycurl_param_element. /// the ancestor class of etherogeneous list/map class mycurl_param_element_generic { public: virtual ~mycurl_param_element_generic() = default; virtual bool operator == (const mycurl_param_element_generic & val) const = 0; virtual bool operator != (const mycurl_param_element_generic & val) const { return ! (*this == val); }; virtual std::unique_ptr clone() const = 0; }; /// the implemented inherited classes of the abstracted class for etherogeneous list/map template class mycurl_param_element: public mycurl_param_element_generic { public: mycurl_param_element(const T & arg): val(arg) {}; mycurl_param_element(const mycurl_param_element & e): val(e.val) {}; mycurl_param_element(mycurl_param_element && e) noexcept: val(std::move(e.val)) {}; mycurl_param_element & operator = (const mycurl_param_element & e) { val = e.val; return this; }; mycurl_param_element & operator = (mycurl_param_element && e) { val = std::move(e.val); return this; }; ~mycurl_param_element() = default; virtual bool operator == (const mycurl_param_element_generic & arg) const override { const mycurl_param_element* arg_ptr = dynamic_cast*>(&arg); if(arg_ptr == nullptr) return false; return arg_ptr->val == val; } T get_value() const { return val; }; const T* get_value_address() const { return &val; }; void set_value(const T & arg) { val = arg; }; virtual std::unique_ptr clone() const override { std::unique_ptr ret; try { ret = std::make_unique >(val); if(!ret) throw Ememory("mycurl_param_list::clone"); } catch(...) { throw Ememory("mycurl_param_list::clone"); } return ret; }; private: T val; }; /// This class holds an etherogenous list, more precisely a map that associates CURLoption option /// to a value of a random type corresponding to each CURLoption option. class mycurl_param_list { public: #ifndef LIBCURL_AVAILABLE // libcurl not available, no field to initialize mycurl_param_list() {}; #else mycurl_param_list() { reset_read(); }; // fields "element_list" and "cursor" are objects and get initialized by their default constructor mycurl_param_list(const mycurl_param_list & ref) { copy_from(ref); }; mycurl_param_list(mycurl_param_list && ref) noexcept = default; mycurl_param_list & operator = (const mycurl_param_list & ref) { element_list.clear(); copy_from(ref); reset_read(); return *this; }; mycurl_param_list & operator = (mycurl_param_list && ref) = default; ~mycurl_param_list() = default; // operations with the list template void add(CURLoption opt, const T & val) { element_list[opt] = std::make_unique >(val); reset_read(); } void clear(CURLoption opt); void clear() { element_list.clear(); reset_read(); }; U_I size() const { return element_list.size(); }; void reset_read() const { cursor = element_list.begin(); }; bool read_next(CURLoption & opt); template void read_opt(const T* & val) const { if(cursor == element_list.end()) throw Erange("mycurl_param_list::read_opt", "Cannot read option when no more option is available"); if(cursor->second) { const mycurl_param_element* ptr = dynamic_cast*>(cursor->second.get()); if(ptr != nullptr) val = ptr->get_value_address(); else val = nullptr; } else val = nullptr; ++cursor; } templatebool get_val(CURLoption opt, const T* & val) const { std::map >::const_iterator it = element_list.find(opt); if(it == element_list.end()) return false; if(it->second) { const mycurl_param_element* ptr = dynamic_cast*>(it->second.get()); if(ptr != nullptr) val = ptr->get_value_address(); else val = nullptr; } else val = nullptr; return true; } // operations between lists /// this method update the current object with parameters from wanted and returns /// the list of modified options /// \note if a CURLoption in wanted is not present in 'this' it is added with the /// associated value and the CURLoption is /// added to the returned list. If a CURLoption is present in both wanted and /// "this" and its value differ between the two lists /// it is updated in "this" and the option is added to the returned list, else /// nothing is changed and the option is not added to the /// returned list. std::list update_with(const mycurl_param_list & wanted); private: std::map > element_list; mutable std::map >::const_iterator cursor; void add_clone(CURLoption opt, const mycurl_param_element_generic & val) { element_list[opt] = val.clone(); } void copy_from(const mycurl_param_list & ref); #endif }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filtre.cpp0000644000175000017500000043640014636067146013336 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_CTYPE_H #include #endif } // end extern "C" #include #include "filtre.hpp" #include "user_interaction.hpp" #include "erreurs_ext.hpp" #include "filesystem_backup.hpp" #include "filesystem_restore.hpp" #include "filesystem_diff.hpp" #include "ea.hpp" #include "defile.hpp" #include "null_file.hpp" #include "thread_cancellation.hpp" #include "compressor.hpp" #include "sparse_file.hpp" #include "semaphore.hpp" #include "deci.hpp" #include "cat_all_entrees.hpp" #include "null_file.hpp" #include "generic_rsync.hpp" #include "cat_signature.hpp" #include "tools.hpp" #include "op_tools.hpp" #include "fichier_global.hpp" #include "capabilities.hpp" using namespace std; #define SKIPPED "Skipping file: " #define SQUEEZED "Ignoring empty directory: " namespace libdar { // returns false if file has changed during backup (inode is saved however, but the saved data may be invalid) // return true if file has not change, false if file need not resaving or does not add wasted bytes in archive // throw exceptions in case of error static bool save_inode(const shared_ptr & dialog,//< how to report to user const string &info_quoi, //< full path name of the file to save (including its name) cat_entree * & e, //< cat_entree to save into the archive const cat_entree * ref, //< reference object if it exists (to grab CRC and delta signature from if present and necessary) const pile_descriptor & pdesc,//< where to write to bool info_details, //< verbose output to user bool display_treated, //< add an information line before treating a file bool alter_atime, //< whether to set back atime of filesystem bool check_change, //< whether to check file change during backup bool compute_crc, //< whether to recompute the CRC 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) const catalogue & cat, //< catalogue to update for escape sequence mark const infinint & repeat_count, //< how much time to retry saving the file if it has changed during the backup const infinint & repeat_byte, //< how much byte remains to waste for saving again a changing file const infinint & hole_size, //< the threshold for hole detection, set to zero completely disable the sparse file detection mechanism semaphore * sem, bool delta_signature, //< if set, lead to delta signature to be computed, if false and delta signature already exists it is saved (which in case of merging leads to transfering it in the resulting archive) bool make_delta_diff, //< whether delta diff is allowed infinint & new_wasted_bytes, //< new amount of wasted bytes to return to the caller. set ignored_as_symlink, //< list of file to ignore as symlink and fetch the proper mtime bool repair_mode, //< if set, try to fix CRC and size problem flagging such fixed files as dirty U_I signature_block_size); //< block size of delta signatures static bool save_ea(const shared_ptr & dialog, const string & info_quoi, cat_inode * & ino, const pile_descriptor & pdesc, bool display_treated, bool repair_mode); static void restore_atime(const string & chemin, const cat_inode * & ptr); static bool save_fsa(const shared_ptr & dialog, const string & info_quoi, cat_inode * & ino, const pile_descriptor & pdesc, bool display_treated, bool repair_mode); /// merge two sets of EA /// \param[in] ref1 is the first EA set /// \param[in] ref2 is the second EA set /// \param[in,out] res is the EA set result of the merging operation /// \note result is the set of EA of ref1 to which those of ref2 are added if not already present in ref1 static void merge_ea(const ea_attributs & ref1, const ea_attributs & ref2, ea_attributs &res); /// to clone an "cat_entree" taking hard links into account /// \param[in] ref is the named entry to be cloned /// \param[in,out] hard_link_base is the datastructure that gather/maps hard_links information /// \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) /// \return a pointer to the new allocated clone object (to be deleted by the delete operator by the caller) static cat_entree *make_clone(const cat_nomme *ref, map & hard_link_base, const infinint & etiquette_offset); /// remove an entry hardlink from a given hard_link database /// \param[in] mir is a pointer to the cat_mirage object to delete /// \param[in,out] hard_link_base is the datastructure used to gather/map hard_links information /// \note if the cat_mirage object is the last one pointing to a given "cat_etoile" object /// deleting this cat_mirage will delete the "cat_etoile" object automatically (see destructor implementation of these classes). /// However, one need to remove from the database the reference to this "cat_etoile *" that is about to me removed by the caller static void clean_hard_link_base_from(const cat_mirage *mir, map & hard_link_base); /// modify the hard_link_base to avoid hole in the numbering of etiquettes (map size == highest etiquette number) static void normalize_link_base(map & hard_link_base); /// transfer EA from one cat_inode to another as defined by the given action /// \param[in] dialog for user interaction /// \param[in] action is the action to perform with EA and FSA /// \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 /// \param[in] to_add is the "to be added" cat_inode /// \note actions set to EA_preserve EA_preserve_mark_already_saved and EA_clear are left intentionnaly unimplemented! /// \note the nullptr given as argument means that the object is not an cat_inode static void do_EFSA_transfert(const shared_ptr &dialog, over_action_ea action, cat_inode *in_place, const cat_inode *to_add); /// overwriting policy when only restoring detruit objects static const crit_action *make_overwriting_for_only_deleted(); /// write down delta signature for unsaved files taking it from entry of reference or computing from filesystem static void save_delta_signature(const shared_ptr & dialog, const string & info_quoi, cat_file * e_file, const cat_file * ref_file, const pile_descriptor & pdesc, U_I signature_block_size, bool info_details, bool display_treated, const catalogue & cat); static bool furtive_check(bool furtive, const shared_ptr & dialog, bool verbose); void filtre_restore(const shared_ptr & dialog, 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, 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, bool ignore_unix_sockets) { defile juillet = fs_racine; // 'juillet' is in reference to 14th of July ;-) when takes place the "defile'" on the Champs-Elysees. const cat_eod tmp_eod; const cat_entree *e; thread_cancellation thr_cancel; const crit_action * when_only_deleted = only_deleted ? make_overwriting_for_only_deleted() : nullptr; const crit_action & overwrite = only_deleted ? *when_only_deleted : x_overwrite; if(!dialog) throw SRC_BUG; // dialog points to nothing if(display_treated_only_dir && display_treated) display_treated = false; // avoid having filesystem to report action performed for each entry // specific code in this function will show instead the current directory // under which file are processed else display_treated_only_dir = false; // avoid incoherence try { filesystem_restore fs(dialog, fs_racine, fs_warn_overwrite, display_treated, ea_mask, what_to_check, warn_remove_no_match, empty, &overwrite, only_deleted, scope); // if only_deleted, we set the filesystem to only overwrite mode (no creatation if not existing) // we also filter to only restore directories and detruit objects. st.clear(); cat.reset_read(); if(!empty_dir) cat.launch_recursive_has_changed_update(); while(cat.read(e)) { const cat_nomme *e_nom = dynamic_cast(e); const cat_directory *e_dir = dynamic_cast(e); const cat_mirage *e_mir = dynamic_cast(e); const cat_inode *e_ino = dynamic_cast(e); const cat_file *e_file = dynamic_cast(e); const cat_detruit *e_det = dynamic_cast(e); const cat_prise *e_pri = dynamic_cast(e); if(e_mir != nullptr) { e_ino = e_mir->get_inode(); if(e_ino == nullptr) throw SRC_BUG; // !?! how is this possible ? e_mir->get_inode()->change_name(e_mir->get_name()); // temporarily changing the inode name to the one of the cat_mirage e_file = dynamic_cast(e_ino); } if(e_det != nullptr && not_deleted) continue; // skip this cat_detruit object juillet.enfile(e); thr_cancel.check_self_cancellation(); if(display_treated_only_dir) { if(e_dir != nullptr) dialog->message(string(gettext("Inspecting directory ")) + juillet.get_string()); } if(e_nom != nullptr) { try { bool path_covered = subtree.is_covered(juillet.get_path()); // is current entry covered by filters (path) bool name_covered = e_dir != nullptr || filtre.is_covered(e_nom->get_name()); // is current entry's filename covered (not applied to directories) bool dirty_covered = e_file == nullptr || !e_file->is_dirty() || dirty != archive_options_extract::dirty_ignore; // checking against dirty files 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 bool flat_covered = e_dir == nullptr || !flat; // we do not restore directories in flat mode bool only_deleted_covered = !only_deleted || e_dir != nullptr || e_det != nullptr; // we do not restore other thing than directories and cat_detruits when in "only_deleted" mode bool socket_covered = ! ignore_unix_sockets || e_pri == nullptr; if(path_covered && name_covered && dirty_covered && empty_dir_covered && flat_covered && only_deleted_covered && socket_covered) { 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) bool ea_restored = false; // will be true if file's EA have been restored (depending on overwriting policy) 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 bool fsa_restored = false; // will be true if file's FSA have been restored (depending on overwriting policy) 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). bool created_retry; // when restoring several dirty copies (in sequential read), we not forget whether the file // has been created or not the first time (which is kept in "created"), for each try, we thus use created_retry // to carry the info whether the file has been created at each try struct cached_Erange { bool active; string source; string message; cached_Erange() { active = false; }; } tmp_exc; // used to hold exception information, when restoring in sequential reading in the hope another copy is available for the file if(dirty == archive_options_extract::dirty_warn && e_file != nullptr && e_file->is_dirty()) { string tmp = juillet.get_string(); 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)); } do { if(!first_time) // a second time only occures in sequential read mode { const cat_file *e_file = dynamic_cast(e_ino); if(info_details) dialog->message(string(gettext("File had changed during backup and had been copied another time, restoring the next copy of file: ")) + juillet.get_string()); // we must let the filesystem object forget that // this hard linked inode has already been seen if(e_mir != nullptr) fs.clear_corres_if_pointing_to(e_mir->get_etiquette(), juillet.get_string()); if(e_file != nullptr) { cat_file *me_file = const_cast(e_file); if(me_file == nullptr) throw SRC_BUG; me_file->drop_crc(); me_file->set_storage_size(0); if(cat.get_escape_layer() == nullptr) throw SRC_BUG; me_file->set_offset(cat.get_escape_layer()->get_position()); } fs.ignore_overwrite_restrictions_for_next_write(); } try { fs.write(e, data_restored, ea_restored, created_retry, hard_link, fsa_restored); // e and not e_ino, it may be a hard link if(tmp_exc.active) { // restoration succeeded so we drop any pending exception tmp_exc.active = false; } } catch(Erange & e) { // we do not throw the exception right now, but // we let a chance to read a new copy (retry upon change) // of that file. Only if there is no more copy available // we will throw this exception (which we cannot not // at this time if following sequential reading mode. tmp_exc.active = true; tmp_exc.source = e.get_source(); tmp_exc.message = e.get_message(); } if(first_time) first_time = false; // Now checking whether there is not a second copy // of the file to use instead, due to file change // while being read for backup. The last copy is the one // to use for restoration. } 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); if(tmp_exc.active) throw Erange(tmp_exc.source, tmp_exc.message); if(cat.get_escape_layer() != nullptr && cat.get_escape_layer()->skip_to_next_mark(escape::seqt_dirty, false)) { string tmp = juillet.get_string(); // file is dirty and we are reading archive sequentially, thus this is only now once we have restored the file that // we can inform the user about the dirtyness of the file. if(e_nom == nullptr) throw SRC_BUG; cat_detruit killer = *e_nom; bool tmp_ea_restored, tmp_created_retry, tmp_hard_link, tmp_fsa_restored; filesystem_restore::action_done_for_data tmp_data_restored; switch(dirty) { case archive_options_extract::dirty_warn: 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)); // NO BREAK HERE !!! This is intended. case archive_options_extract::dirty_ignore: // we must remove the file if(info_details) { if(dirty == archive_options_extract::dirty_ignore) dialog->message(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)); else dialog->message(tools_printf(gettext("Removing the dirty file %S"), &tmp)); } fs.ignore_overwrite_restrictions_for_next_write(); fs.write(&killer, tmp_data_restored, tmp_ea_restored, tmp_created_retry, tmp_hard_link, tmp_fsa_restored); break; case archive_options_extract::dirty_ok: break; default: throw SRC_BUG; } } // Now updating the statistics counters if(hard_link) st.incr_hard_links(); switch(data_restored) { case filesystem_restore::done_data_restored: if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_treated(); break; case filesystem_restore::done_no_change_no_data: st.incr_skipped(); break; case filesystem_restore::done_no_change_policy: st.incr_tooold(); break; case filesystem_restore::done_data_removed: st.incr_deleted(); break; default: throw SRC_BUG; } if(ea_restored) st.incr_ea_treated(); if(fsa_restored) st.incr_fsa_treated(); } else // object not covered by filters { if(display_skipped) { if(!path_covered || !name_covered || !dirty_covered || !socket_covered) dialog->message(string(gettext(SKIPPED)) + juillet.get_string()); else dialog->message(string(gettext(SQUEEZED)) + juillet.get_string()); } if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_ignored(); if(e_dir != nullptr) { if(!path_covered || !empty_dir_covered) { // this directory has been excluded by path_covered // or empty_dir_covered. We must not recurse in it // (this is not a flat restoration for example) cat.skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } } } } catch(Ebug & e) { throw; } catch(Euser_abort & e) { dialog->message(juillet.get_string() + gettext(" not restored (user choice)")); if(e_dir != nullptr && !flat) { dialog->message(gettext("No file in this directory will be restored.")); cat.skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_ignored(); } catch(Ethread_cancel & e) { throw; } catch(Escript & e) { throw; } catch(Egeneric & e) { if(!only_deleted || e_dir == nullptr) dialog->message(string(gettext("Error while restoring ")) + juillet.get_string() + " : " + e.get_message()); if(e_dir != nullptr && !flat) { if(!only_deleted) dialog->message(string(gettext("Warning! No file in that directory will be restored: ")) + juillet.get_string()); cat.skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } if(e_dir == nullptr || !cat.read_second_time_dir()) if(!only_deleted) st.incr_errored(); } // avoiding keeping in memory the delta signature // is is loaded in any case when in sequential read mode // it may be loaded when needed in direct access mode // it is not stored in a catalogue, thus dropping it now // still allows on-fly isolation later on with that catalogue if(e_file != nullptr) e_file->drop_delta_signature_data(); } else // e_nom == nullptr : this should be a CAT_EOD { const cat_eod *e_eod = dynamic_cast(e); if(e_eod == nullptr) throw SRC_BUG; // not an class cat_eod object, nor a class cat_nomme object ??? if(!flat) { bool notusedhere; filesystem_restore::action_done_for_data tmp; fs.write(e, tmp, notusedhere, notusedhere, notusedhere, notusedhere); // cat_eod; don't care returned value } } } } catch(...) { if(when_only_deleted != nullptr) delete when_only_deleted; throw; } if(when_only_deleted != nullptr) delete when_only_deleted; } void filtre_sauvegarde(const shared_ptr & dialog, 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, const filesystem_ids & same_fs, 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 delta_signature, const infinint & delta_sig_min_size, const mask & delta_mask, bool delta_diff, bool auto_zeroing_neg_dates, const set & ignored_symlinks, modified_data_detection mod_data_detect, const delta_sig_block_size & delta_sig_block_len) { if(!dialog) throw SRC_BUG; // dialog points to nothing cat_entree *e = nullptr; const cat_entree *f = nullptr; defile juillet = fs_racine; const cat_eod tmp_eod; compression stock_algo; semaphore sem = semaphore(dialog, backup_hook_file_execute, backup_hook_file_mask); if(display_treated_only_dir && display_treated) display_treated = false; // avoid having save_inode/save_ea to report action performed for each entry // specific code in this function will show instead the current directory // under which file are processed else display_treated_only_dir = false; // avoid incoherence stock_algo = pdesc.compr->get_algo(); infinint root_fs_device; filesystem_backup fs(dialog, fs_racine, info_details, ea_mask, nodump, alter_atime, furtive_check(furtive_read_mode, dialog, info_details), cache_directory_tagging, root_fs_device, ignore_unknown, scope); thread_cancellation thr_cancel; infinint skipped_dump, fs_errors; infinint wasted_bytes = 0; if(auto_zeroing_neg_dates) fs.zeroing_negative_dates_without_asking(); st.clear(); cat.reset_add(); ref.reset_compare(); fs.set_ignored_symlinks_list(ignored_symlinks); try { try { while(fs.read(e, fs_errors, skipped_dump)) { cat_nomme *nom = dynamic_cast(e); cat_directory *dir = dynamic_cast(e); cat_inode *e_ino = dynamic_cast(e); cat_file *e_file = dynamic_cast(e); cat_mirage *e_mir = dynamic_cast(e); bool known_hard_link = false; st.add_to_ignored(skipped_dump); st.add_to_errored(fs_errors); juillet.enfile(e); thr_cancel.check_self_cancellation(); if(display_treated_only_dir) { if(dir != nullptr) dialog->message(string(gettext("Inspecting directory ")) + juillet.get_string()); } if(e_mir != nullptr) { known_hard_link = e_mir->is_inode_wrote(); if(!known_hard_link) { e_ino = dynamic_cast(e_mir->get_inode()); e_file = dynamic_cast(e_mir->get_inode()); if(e_ino == nullptr) throw SRC_BUG; e_ino->change_name(e_mir->get_name()); } } if(nom != nullptr) { try { string tmp_val; if(subtree.is_covered(juillet.get_path()) && (dir != nullptr || filtre.is_covered(nom->get_name())) && (e_ino == nullptr || same_fs.is_covered(e_ino->get_device())) && (e_ino == nullptr || exclude_by_ea == "" || e_ino->ea_get_saved_status() != ea_saved_status::full || e_ino->get_ea() == nullptr || !e_ino->get_ea()->find(exclude_by_ea, tmp_val))) { if(known_hard_link) { // no need to update the semaphore here as no data is read and no directory is expected here cat.pre_add(e); // if cat is a escape_catalogue, this adds an escape sequence and entry info in the archive cat.add(e); e = nullptr; st.incr_hard_links(); st.incr_treated(); if(e_mir != nullptr) { if(e_mir->get_inode()->get_saved_status() == saved_status::saved || e_mir->get_inode()->get_saved_status() == saved_status::delta || e_mir->get_inode()->ea_get_saved_status() == ea_saved_status::full) if(display_treated) dialog->message(string(gettext("Recording hard link into the archive: "))+juillet.get_string()); } else throw SRC_BUG; // known_hard_link is true and e_mir == nullptr !??? } else // not a hard link nor a known hard linked inode { const cat_inode *f_ino = nullptr; const cat_file *f_file = nullptr; const cat_mirage *f_mir = nullptr; if(e_ino == nullptr) throw SRC_BUG; // if not a known hard link, e_ino should still either point to a real cat_inode // or to the hard linked new cat_inode. if(fixed_date.is_zero()) { bool conflict = ref.compare(e, f); if(!conflict) { f = nullptr; f_ino = nullptr; f_mir = nullptr; } else // inode was already present in filesystem at the time the archive of reference was made { f_ino = dynamic_cast(f); f_file = dynamic_cast(f); f_mir = dynamic_cast(f); if(f_mir != nullptr) { f_ino = f_mir->get_inode(); f_file = dynamic_cast(f_ino); } // Now checking for filesystem dissimulated modifications if(security_check) { if(f_ino != nullptr && e_ino != nullptr) { // both are inodes if(cat_signature::compatible_signature(f_ino->signature(), e_ino->signature()) // both are of the same type of inode && f_file != nullptr) // both are plain files (no warning issued for other inode types) { // both are plain file or hard-linked plain files if(f_ino->get_uid() == e_ino->get_uid() && f_ino->get_gid() == e_ino->get_gid() && f_ino->get_perm() == e_ino->get_perm() && f_ino->get_last_modif() == e_ino->get_last_modif()) // no datetime::loose_equal() here! { // same inode information if(f_ino->has_last_change() && e_ino->has_last_change()) { // both inode ctime has been recorded if(!f_ino->get_last_change().loose_equal(e_ino->get_last_change())) { string tmp = juillet.get_string(); dialog->printf(gettext("SECURITY WARNING! SUSPICIOUS FILE %S: ctime changed since archive of reference was done, while no other inode information changed"), &tmp); } } } } } } } } else f = nullptr; try { U_I sig_bl; // stores the delta sig block len for this file f_ino = snapshot ? nullptr : f_ino; f_file = snapshot ? nullptr : f_file; // EVALUATING THE ACTION TO PERFORM bool change_to_remove_ea = e_ino != nullptr && e_ino->ea_get_saved_status() == ea_saved_status::none // current inode to backup does not have any EA && f_ino != nullptr && f_ino->ea_get_saved_status() != ea_saved_status::none && f_ino->ea_get_saved_status() != ea_saved_status::removed; // and reference was an inode with EA bool avoid_saving_inode = snapshot // don't backup if doing a snapshot || (!fixed_date.is_zero() && e_ino != nullptr && e_ino->get_last_modif() < fixed_date) // don't backup if older than given date (if reference date given) || (fixed_date.is_zero() && e_ino != nullptr && f_ino != nullptr && !e_ino->has_changed_since(*f_ino, hourshift, what_to_check) && (f_file == nullptr || !f_file->is_dirty())) // don't backup if doing differential backup and entry is the same as the one in the archive of reference // and if the reference is a plain file, it was not saved as dirty ; bool make_delta_diff = !avoid_saving_inode && delta_diff && e_file != nullptr && f_file != nullptr && f_file->has_delta_signature_available(); // will fail in sequential read mode as the delta_sig of f_file is not been loaded at that time bool avoid_saving_ea = snapshot // don't backup if doing a snapshot || (!fixed_date.is_zero() && e_ino != nullptr && e_ino->ea_get_saved_status() != ea_saved_status::none && e_ino->get_last_change() < fixed_date) // don't backup if older than given date (if reference date given) || (fixed_date.is_zero() && e_ino != nullptr && e_ino->ea_get_saved_status() == ea_saved_status::full && f_ino != nullptr && f_ino->ea_get_saved_status() != ea_saved_status::none && (e_ino->get_last_change() <= f_ino->get_last_change() || e_ino->get_last_change().loose_equal(f_ino->get_last_change())) ) // don't backup if doing differential backup and entry is the same as the one in the archive of reference ; bool avoid_saving_fsa = snapshot // don't backup if doing a snapshot || (!fixed_date.is_zero() && e_ino != nullptr && e_ino->fsa_get_saved_status() != fsa_saved_status::none && e_ino->get_last_change() < fixed_date) // don't backup if older than given date (if reference date given) || (fixed_date.is_zero() && e_ino != nullptr && e_ino->fsa_get_saved_status() == fsa_saved_status::full && f_ino != nullptr && f_ino->fsa_get_saved_status() != fsa_saved_status::none && (e_ino->get_last_change() <= f_ino->get_last_change() || e_ino->get_last_change().loose_equal(f_ino->get_last_change())) ) // don't backup if doing differential backup and entry is the same as the one in the archive of reference ; bool sparse_file_detection = e_file != nullptr && e_file->get_size() > sparse_file_min_size && !sparse_file_min_size.is_zero(); // MODIFIYING INODE IF NECESSARY if(e_ino->get_saved_status() != saved_status::saved) throw SRC_BUG; // filsystem should always provide "saved" "cat_entree" if(avoid_saving_inode) { e_ino->set_saved_status(saved_status::not_saved); st.incr_skipped(); } else // checking whether we can leverage the inode_only status { if(f_file != nullptr && e_file != nullptr) { bool same_data = false; // the whole inode is about to be resaved // checking whether data has really changed // or whether the change only concerns metadata switch(mod_data_detect) { case modified_data_detection::any_inode_change: break; case modified_data_detection::mtime_size: same_data = e_file->same_data_as(*f_file, false, hourshift); break; default: throw SRC_BUG; } if(same_data) { e_file->set_saved_status(saved_status::inode_only); make_delta_diff = false; avoid_saving_inode = true; st.incr_inode_only(); } } // nothing to do else } if(make_delta_diff) e_ino->set_saved_status(saved_status::delta); if(avoid_saving_ea) { if(e_ino->ea_get_saved_status() == ea_saved_status::full) e_ino->ea_set_saved_status(ea_saved_status::partial); } if(avoid_saving_fsa) { if(e_ino->fsa_get_saved_status() == fsa_saved_status::full) e_ino->fsa_set_saved_status(fsa_saved_status::partial); } if(change_to_remove_ea) e_ino->ea_set_saved_status(ea_saved_status::removed); if(e_file != nullptr) { if(make_delta_diff) // sparse_file layer is not useful // when performing a delta patch e_file->set_sparse_file_detection_write(false); else e_file->set_sparse_file_detection_write(sparse_file_detection); } // DECIDING WHETHER FILE DATA WILL BE COMPRESSED OR NOT if(e_file != nullptr) { if(compr_mask.is_covered(nom->get_name()) && e_file->get_size() >= min_compr_size) // e_nom not e_file because "e" // may be a hard link, in which case its name is not carried by e_ino nor e_file e_file->change_compression_algo_write(stock_algo); else e_file->change_compression_algo_write(compression::none); } // DECIDING WHETHER DELTA SIGNATURE WILL BE CALCULATED if(e_file != nullptr && delta_signature && delta_mask.is_covered(juillet.get_string()) && e_file->get_size() >= delta_sig_min_size) { e_file->will_have_delta_signature_available(); // during small inode dump for that file, the flag telling a delta_sig is present will be set // CALCULATING THE SIGNATURE BLOCK SIZE if(e_file != nullptr) sig_bl = delta_sig_block_len.calculate(e_file->get_size()); else sig_bl = 0; } else sig_bl = 0; // PERFORMING ACTION FOR ENTRY (cat_entree dump, eventually data dump) if(!save_inode(dialog, juillet.get_string(), e, f, pdesc, info_details, display_treated, alter_atime, true, // check_change true, // compute_crc cat_file::normal, // keep_mode cat, repeat_count, repeat_byte, sparse_file_min_size, &sem, e_file == nullptr ? false : e_file->has_delta_signature_available(), make_delta_diff, wasted_bytes, ignored_symlinks, false, sig_bl)) st.incr_tooold(); // counting a new dirty file in archive st.set_byte_amount(wasted_bytes); if(!avoid_saving_inode) st.incr_treated(); // PERFORMING ACTION FOR EA if(e_ino->ea_get_saved_status() != ea_saved_status::removed) { if(e_ino->ea_get_saved_status() == ea_saved_status::full) cat.pre_add_ea(e); if(save_ea(dialog, juillet.get_string(), e_ino, pdesc, display_treated, false)) st.incr_ea_treated(); cat.pre_add_ea_crc(e); } // PERFORMING ACTION FOR FSA if(e_ino->fsa_get_saved_status() == fsa_saved_status::full) { cat.pre_add_fsa(e); if(save_fsa(dialog, juillet.get_string(), e_ino, pdesc, display_treated, false)) st.incr_fsa_treated(); cat.pre_add_fsa_crc(e); } // CLEANING UP MEMORY FOR PLAIN FILES if(e_file != nullptr) e_file->clean_data(); // UPDATING HARD LINKS if(e_mir != nullptr) e_mir->set_inode_wrote(true); // record that this inode has been saved (it is a "known_hard_link" now) // ADDING ENTRY TO CATALOGUE cat.add(e); e = nullptr; } catch(...) { if(dir != nullptr && fixed_date.is_zero()) ref.compare(&tmp_eod, f); throw; } } // the object is kept in the catalogue, reducing memory footprint // avoiding keeping in memory the delta signature data if(e_file != nullptr) e_file->drop_delta_signature_data(); } else // inode not covered { cat_nomme *ig = nullptr; cat_inode *ignode = nullptr; sem.raise(juillet.get_string(), e, false); if(display_skipped) dialog->message(string(gettext(SKIPPED)) + juillet.get_string()); if(dir != nullptr && make_empty_dir) ig = ignode = new (nothrow) cat_ignored_dir(*dir); else ig = new (nothrow) cat_ignored(nom->get_name()); // necessary to not record deleted files at comparison // time in case files are just not covered by filters st.incr_ignored(); if(ig == nullptr) throw Ememory("filtre_sauvegarde"); else cat.add(ig); if(dir != nullptr) { if(make_empty_dir) { bool known; if(fixed_date.is_zero()) known = ref.compare(dir, f); else known = false; try { const cat_inode *f_ino = known ? dynamic_cast(f) : nullptr; bool tosave = false; if(known) if(f_ino != nullptr) tosave = dir->has_changed_since(*f_ino, hourshift, what_to_check); else throw SRC_BUG; // catalogue::compare() with a directory should return false or give a directory as // second argument or here f is not an inode (f_ino == nullptr) ! // and known == true else tosave = true; if(ignode == nullptr) throw SRC_BUG; ignode->set_saved_status(tosave && !snapshot ? saved_status::saved : saved_status::not_saved); // this creates the escape sequence mark cat.pre_add(ignode); cat.pre_add(&tmp_eod); } catch(...) { if(fixed_date.is_zero()) ref.compare(&tmp_eod, f); throw; } if(fixed_date.is_zero()) ref.compare(&tmp_eod, f); } fs.skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } delete e; // we don't keep this object in the catalogue so we must destroy it e = nullptr; } } catch(Ebug & e) { throw; } catch(Euser_abort & e) { throw; } catch(Escript & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Elimitint & e) { throw; } catch(Egeneric & ex) { const string & how = ex.find_object("generic_file::copy_to"); if(how != "write") // error did not occured while adding data to the archive { cat_nomme *tmp = new (nothrow) cat_ignored(nom->get_name()); dialog->message(string(gettext("Error while saving ")) + juillet.get_string() + ": " + ex.get_message()); st.incr_errored(); // now we can destroy the object delete e; e = nullptr; if(tmp == nullptr) throw Ememory("fitre_sauvegarde"); cat.add(tmp); if(dir != nullptr) { fs.skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); dialog->message(gettext("NO FILE IN THAT DIRECTORY CAN BE SAVED.")); } } else { ex.prepend_message(gettext("Cannot write down the archive: ")); throw; // error occured while adding data to the archive, archive cannot be generated properly } } } else // cat_eod { sem.raise(juillet.get_string(), e, true); sem.lower(); if(fixed_date.is_zero()) ref.compare(e, f); // makes the comparison in the reference catalogue go to parent directory 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) if(display_finished) { const cat_directory & cur = cat.get_current_add_dir(); string what = juillet.get_string(); string size = tools_display_integer_in_metric_system(cur.get_size(), "o", true); string ratio = gettext(", compression ratio "); if(!cur.get_storage_size().is_zero()) ratio += tools_get_compression_ratio(cur.get_storage_size(), cur.get_size(), true); else ratio = ""; dialog->printf(gettext("Finished Inspecting directory %S , saved %S%S"), &what, &size, &ratio); } cat.add(e); } } } catch(Ethread_cancel & e) { if(!e.immediate_cancel()) { if(pdesc.compr->is_compression_suspended()) { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->resume_compression(); } } throw Ethread_cancel_with_attr(e.immediate_cancel(), e.get_flag(), fs.get_last_etoile_ref()); } } catch(...) { if(e != nullptr) delete e; throw; } if(pdesc.compr->is_compression_suspended()) { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->resume_compression(); } } void filtre_difference(const shared_ptr & dialog, 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, comparison_fields what_to_check, const infinint & hourshift, bool compare_symlink_date, const fsa_scope & scope, bool isolated_mode) { if(!dialog) throw SRC_BUG; // dialog points to nothing const cat_entree *e; defile juillet = fs_racine; const cat_eod tmp_eod; filesystem_diff fs(dialog, fs_racine, info_details, ea_mask, alter_atime, furtive_check(furtive_read_mode, dialog, info_details), scope); thread_cancellation thr_cancel; if(display_treated_only_dir && display_treated) display_treated = false; // avoiding the report of action performed for each entry // specific code in this function will show instead the current directory // under which file are processed else display_treated_only_dir = false; // avoid incoherence st.clear(); cat.reset_read(); while(cat.read(e)) { const cat_directory *e_dir = dynamic_cast(e); const cat_nomme *e_nom = dynamic_cast(e); const cat_inode *e_ino = dynamic_cast(e); const cat_mirage *e_mir = dynamic_cast(e); if(e_mir != nullptr) { const cat_file *e_file = dynamic_cast(e_mir->get_inode()); if(e_file == nullptr || e_mir->get_etoile_ref_count() == 1 || cat.get_escape_layer() == nullptr) { e_ino = e_mir->get_inode(); e_mir->get_inode()->change_name(e_mir->get_name()); } else dialog->message(gettext("SKIPPED (hard link in sequential read mode): ") + e_mir->get_name()); } juillet.enfile(e); thr_cancel.check_self_cancellation(); if(display_treated_only_dir) { if(e_dir != nullptr) dialog->message(string(gettext("Inspecting directory ")) + juillet.get_string()); } try { if(e_nom != nullptr) { if(subtree.is_covered(juillet.get_path()) && (e_dir != nullptr || filtre.is_covered(e_nom->get_name()))) { cat_nomme *exists_nom = nullptr; if(e_ino != nullptr) { if(e_nom == nullptr) throw SRC_BUG; if(fs.read_filename(e_nom->get_name(), exists_nom)) { try { cat_inode *exists = dynamic_cast(exists_nom); cat_directory *exists_dir = dynamic_cast(exists_nom); if(exists != nullptr) { try { e_ino->compare(*exists, ea_mask, what_to_check, hourshift, compare_symlink_date, scope, isolated_mode); if(display_treated) dialog->message(string(gettext("OK "))+juillet.get_string()); if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_treated(); if(!alter_atime) { const cat_inode * tmp_exists = exists; restore_atime(juillet.get_string(), tmp_exists); } } catch(Erange & e) { dialog->message(string(gettext("DIFF "))+juillet.get_string()+": "+ e.get_message()); if(e_dir == nullptr && exists_dir != nullptr) fs.skip_read_filename_in_parent_dir(); if(e_dir != nullptr && exists_dir == nullptr) { cat.skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_errored(); if(!alter_atime) { const cat_inode * tmp_exists = exists; restore_atime(juillet.get_string(), tmp_exists); } } } else // existing file is not an inode throw SRC_BUG; // filesystem, should always return inode with read_filename() } catch(...) { delete exists_nom; exists_nom = nullptr; throw; } delete exists_nom; exists_nom = nullptr; } else // can't compare, nothing of that name in filesystem { dialog->message(string(gettext("DIFF "))+ juillet.get_string() + gettext(": file not present in filesystem")); if(e_dir != nullptr) { cat.skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_errored(); } } else // not an inode (for example a cat_detruit, hard_link etc...), nothing to do st.incr_treated(); } else // not covered by filters { if(display_skipped) dialog->message(string(gettext(SKIPPED)) + juillet.get_string()); if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_ignored(); if(e_dir != nullptr) { cat.skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } } } else // cat_eod ? if(dynamic_cast(e) != nullptr) // yes cat_eod fs.skip_read_filename_in_parent_dir(); else // no ?!? throw SRC_BUG; // not cat_nomme neither cat_eod ! what's that ? } catch(Euser_abort &e) { throw; } catch(Ebug &e) { throw; } catch(Escript & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { dialog->message(string(gettext("ERR ")) + juillet.get_string() + " : " + e.get_message()); if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_errored(); } } fs.skip_read_filename_in_parent_dir(); // this call here only to restore dates of the root (-R option) directory } void filtre_test(const shared_ptr & dialog, 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) { const cat_entree *e; defile juillet = FAKE_ROOT; null_file black_hole = null_file(gf_write_only); infinint offset; const cat_eod tmp_eod; thread_cancellation thr_cancel; string perimeter; shared_ptr delta_sig; U_I sig_block_len; if(!dialog) throw SRC_BUG; // dialog points to nothing if(display_treated_only_dir && display_treated) display_treated = false; // avoid having save_inode/save_ea to report action performed for each entry // specific code in this function will show instead the current directory // under which file are processed else display_treated_only_dir = false; // avoid incoherence st.clear(); cat.set_all_mirage_s_inode_wrote_field_to(false); cat.reset_read(); while(cat.read(e)) { const cat_file *e_file = dynamic_cast(e); const cat_inode *e_ino = dynamic_cast(e); const cat_directory *e_dir = dynamic_cast(e); const cat_nomme *e_nom = dynamic_cast(e); const cat_mirage *e_mir = dynamic_cast(e); juillet.enfile(e); thr_cancel.check_self_cancellation(); if(display_treated_only_dir) { if(e_dir != nullptr) dialog->message(string(gettext("Inspecting directory ")) + juillet.get_string()); } perimeter = ""; try { if(e_mir != nullptr) { if(!e_mir->is_inode_wrote()) { e_file = dynamic_cast(e_mir->get_inode()); e_ino = e_mir->get_inode(); } } if(e_nom != nullptr) { if(subtree.is_covered(juillet.get_path()) && (e_dir != nullptr || filtre.is_covered(e_nom->get_name()))) { // checking data file if any if(e_file != nullptr && (e_file->get_saved_status() == saved_status::saved || e_file->get_saved_status() == saved_status::delta)) { perimeter = gettext("Data"); if(!empty) { bool dirty_file; do { generic_file *dat = e_file->get_data(cat_file::normal, nullptr, 0, nullptr); if(dat == nullptr) throw Erange("filtre_test", gettext("Can't read saved data.")); dirty_file = false; try { infinint crc_size; crc *check = nullptr; const crc *original = nullptr; if(!e_file->get_crc_size(crc_size)) crc_size = tools_file_size_to_crc_size(e_file->get_size()); dat->skip(0); // in sequential read mode, storage_size is zero // which leads to ask an endless read_ahead (up to eof) // thus the read_ahaead will be bounded by the escape // layer up to the next tape mark, as expected dat->read_ahead(e_file->get_storage_size()); try { dat->copy_to(black_hole, crc_size, check); } catch(...) { // in sequential read mode we must // try to read the CRC for the object // be completed and not generating an // error due to absence of CRC later on e_file->get_crc(original); throw; } if(check == nullptr) throw SRC_BUG; try { // due to possible sequential reading mode, the CRC // must not be fetched before the data has been copied if(e_file->get_crc(original)) { if(original == nullptr) throw SRC_BUG; if(typeid(*check) != typeid(*original)) throw SRC_BUG; if(*check != *original) throw Erange("fitre_test", gettext("CRC error: data corruption.")); } } catch(...) { delete check; throw; } delete check; } catch(...) { delete dat; throw; } delete dat; if(cat.get_escape_layer() != nullptr && cat.get_escape_layer()->skip_to_next_mark(escape::seqt_changed, false)) { dirty_file = true; cat_file *modif_e_file = const_cast(e_file); if(modif_e_file == nullptr) throw SRC_BUG; modif_e_file->drop_crc(); modif_e_file->set_storage_size(0); modif_e_file->set_offset(cat.get_escape_layer()->get_position()); } } while(dirty_file); } } // checking delta signature if any if(e_file != nullptr && e_file->has_delta_signature_structure()) { try { // reading the delta signature e_file->read_delta_signature(delta_sig, sig_block_len); if(delta_sig) delta_sig.reset(); if(perimeter == "") perimeter = "Delta sig"; else perimeter += " + Delta sig"; } catch(...) { e_file->drop_delta_signature_data(); throw; } e_file->drop_delta_signature_data(); } // checking inode EA if any if(e_ino != nullptr && e_ino->ea_get_saved_status() == ea_saved_status::full && !cat.read_second_time_dir()) { if(perimeter == "") perimeter = "EA"; else perimeter += " + EA"; if(!empty) { ea_attributs tmp = *(e_ino->get_ea()); perimeter += "(" + deci(tmp.size()).human() +")"; e_ino->ea_detach(); } } // checking FSA if any if(e_ino != nullptr && e_ino->fsa_get_saved_status() == fsa_saved_status::full && !cat.read_second_time_dir()) { if(perimeter == "") perimeter = "FSA"; else perimeter += " + FSA"; if(!empty) { const filesystem_specific_attribute_list *tmp = e_ino->get_fsa(); if(tmp == nullptr) throw SRC_BUG; perimeter += "(" + deci(tmp->size()).human() + ")"; e_ino->fsa_detach(); } } if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_treated(); if(e_mir != nullptr) e_mir->set_inode_wrote(true); // still no exception raised, this all is fine if(display_treated) dialog->message(string(gettext("OK ")) + juillet.get_string() + " " + perimeter); } else // excluded by filter { if(display_skipped) dialog->message(string(gettext(SKIPPED)) + juillet.get_string()); if(e_dir != nullptr) { juillet.enfile(&tmp_eod); cat.skip_read_to_parent_dir(); } if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_skipped(); } } } catch(Euser_abort & e) { throw; } catch(Ebug & e) { throw; } catch(Escript & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { dialog->message(string(gettext("ERR ")) + juillet.get_string() + " : " + e.get_message()); if(e_dir == nullptr || !cat.read_second_time_dir()) st.incr_errored(); } } } void filtre_merge(const shared_ptr & dialog, 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, bool delta_signature, bool build_delta_sig, const infinint & delta_sig_min_size, const mask & delta_mask, const delta_sig_block_size & signature_block_size) { crit_action *decr = nullptr; // will point to a locally allocated crit_action // for decremental backup (argument overwrite is ignored) const crit_action *overwrite = &over_action; // may point to &decr if // decremental backup is asked bool abort = false; thread_cancellation thr_cancel; if(!dialog) throw SRC_BUG; // dialog points to nothing if(display_treated_only_dir && display_treated) display_treated = false; // avoiding the report of action performed for each entry // specific code in this function will show instead the current directory // under which file are processed else display_treated_only_dir = false; // avoid incoherence filtre_merge_step0(dialog, ref1, ref2, st, decremental_mode, decr, overwrite, abort, thr_cancel); filtre_merge_step1(dialog, filtre, subtree, cat, ref1, ref2, info_details, display_treated, display_skipped, st, make_empty_dir, warn_overwrite, decremental_mode, decr, overwrite, abort, thr_cancel); filtre_merge_step2(dialog, pdesc, cat, info_details, display_treated, display_treated_only_dir, compr_mask, min_compr_size, keep_compressed, sparse_file_min_size, delta_signature, build_delta_sig, delta_sig_min_size, delta_mask, abort, thr_cancel, false, signature_block_size); } void filtre_merge_step0(const shared_ptr & dialog, const catalogue * ref1, const catalogue * ref2, statistics & st, bool decremental_mode, crit_action* & decr, const crit_action* & overwrite, bool & abort, thread_cancellation & thr_cancel) { if(!dialog) throw SRC_BUG; // dialog points to nothing // STEP 0: Getting ready st.clear(); if(decremental_mode) { if(ref1 == nullptr || ref2 == nullptr) { 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?")); decremental_mode = false; } else { // allocating decr to "{E&R&~R&(A|!H)}[S*] P* ; {(e&~e&r&~r)|(!e&!~e)}[*s] *p" // // which means to record just data presence (S*) when: // both entries are of the same type and share the same inode data (E) // and both have the same modification date (R&~R) // and this is the first time we meet this hard linked inode, or this is not a hard linked inode (A|!H) // else data is taken as is (P*) from the "in place" archive // EA and FSA are recorded as present when: // both entries have EA/FSA present (e&~e) // and both EA/FSA set have the same date (r&~r) // OR // none entry has EA/FSA present // else the EA/FSA (or the absence of EA/FSA) is taken from the "in place" archive try { crit_chain *decr_crit_chain = new (nothrow) crit_chain(); if(decr_crit_chain == nullptr) throw Ememory("filtre_merge"); decr = decr_crit_chain; crit_and c_and = crit_and(); crit_or c_or = crit_or(); c_and.clear(); c_or.clear(); c_and.add_crit(crit_same_inode_data()); c_and.add_crit(crit_in_place_data_more_recent()); c_and.add_crit(crit_invert(crit_in_place_data_more_recent())); c_or.add_crit(crit_in_place_is_new_hardlinked_inode()); c_or.add_crit(crit_not(crit_in_place_is_hardlinked_inode())); c_and.add_crit(c_or); decr_crit_chain->add(testing( c_and, crit_constant_action(data_preserve_mark_already_saved, EA_undefined), crit_constant_action(data_preserve, EA_undefined) )); c_and.clear(); c_or.clear(); c_and.add_crit(crit_in_place_EA_present()); c_and.add_crit(crit_invert(crit_in_place_EA_present())); c_and.add_crit(crit_in_place_EA_more_recent()); c_and.add_crit(crit_invert(crit_in_place_EA_more_recent())); c_or.add_crit(c_and); c_and.clear(); c_and.add_crit(crit_not(crit_in_place_EA_present())); c_and.add_crit(crit_not(crit_invert(crit_in_place_EA_present()))); c_or.add_crit(c_and); decr_crit_chain->add(testing( c_or, crit_constant_action(data_undefined, EA_preserve_mark_already_saved), crit_constant_action(data_undefined, EA_preserve) )); } catch(...) { if(decr != nullptr) { delete decr; decr = nullptr; } throw; } overwrite = decr; } } /// End of Step 0 } void filtre_merge_step1(const shared_ptr & dialog, const mask & filtre, const mask & subtree, catalogue & cat, const catalogue * ref1, const catalogue * ref2, bool info_details, bool display_treated, bool display_skipped, statistics & st, bool make_empty_dir, bool warn_overwrite, bool decremental_mode, crit_action* & decr, const crit_action* & overwrite, bool & abort, thread_cancellation & thr_cancel) { const catalogue *ref_tab[] = { ref1, ref2, nullptr }; const cat_entree *e = nullptr; U_I index = 0; defile juillet = FAKE_ROOT; map corres_copy; infinint etiquette_offset = 0; const cat_eod tmp_eod; try { if(overwrite == nullptr) throw SRC_BUG; if(!dialog) throw SRC_BUG; // dialog points to nothing // STEP 1: // we merge catalogues (pointed to by ref_tab[]) of each archive and produce a resulting catalogue 'cat' // the merging resolves overwriting conflicts following the "criterium" rule // each object of the catalogue is cloned and stored in 'cat', these clones get dump to archive at step 2 try { while(ref_tab[index] != nullptr) // for each catalogue of reference (ref. and eventually auxiliary ref.) do: { juillet = FAKE_ROOT; cat.reset_add(); cat.reset_read(); ref_tab[index]->reset_read(); if(info_details) { const char *ptr; switch(index) { case 0: ptr = gettext("first"); break; case 1: ptr = gettext("second"); break; default: ptr = gettext("next"); // not yet used, but room is made for future evolutions break; } dialog->printf(gettext("Merging/filtering files from the %s archive..."), ptr); } while(ref_tab[index]->read(e)) // examining the content of the current archive of reference, each entry one by one { const cat_nomme *e_nom = dynamic_cast(e); const cat_mirage *e_mir = dynamic_cast(e); const cat_directory *e_dir = dynamic_cast(e); const cat_detruit *e_detruit = dynamic_cast(e); juillet.enfile(e); thr_cancel.check_self_cancellation(); if(e_nom != nullptr) { try { if(subtree.is_covered(juillet.get_path()) && (e_dir != nullptr || filtre.is_covered(e_nom->get_name()))) { cat_entree *dolly = make_clone(e_nom, corres_copy, etiquette_offset); // now that we have a clone object we must add the copied object to the catalogue, respecting the overwriting constaints try { string the_name = e_nom->get_name(); const cat_nomme *already_here = nullptr; // may receive an address when an object of that name already exists in the resultant catalogue // some different types of pointer to the "dolly" object cat_nomme *dolly_nom = dynamic_cast(dolly); cat_directory *dolly_dir = dynamic_cast(dolly); cat_mirage *dolly_mir = dynamic_cast(dolly); cat_inode *dolly_ino = dynamic_cast(dolly); if(dolly_mir != nullptr) dolly_ino = dolly_mir->get_inode(); if(cat.read_if_present(& the_name, already_here)) // An entry of that name already exists in the resulting catalogue { // some different types of pointer to the "already_here" object (aka 'inplace" object) const cat_mirage *al_mir = dynamic_cast(already_here); const cat_detruit *al_det = dynamic_cast(already_here); const cat_ignored *al_ign = dynamic_cast(already_here); const cat_ignored_dir *al_igndir = dynamic_cast(already_here); const cat_inode *al_ino = dynamic_cast(already_here); const cat_directory *al_dir = dynamic_cast(already_here); const string full_name = juillet.get_string(); over_action_data act_data; over_action_ea act_ea; if(al_mir != nullptr) al_ino = al_mir->get_inode(); if(dolly_nom == nullptr) throw SRC_BUG; // dolly should be the clone of a cat_nomme object which is not the case here // evaluating the overwriting policy overwrite->get_action(*already_here, *dolly_nom, act_data, act_ea); if(act_data == data_ask) act_data = op_tools_crit_ask_user_for_data_action(*dialog, full_name, already_here, dolly); // possibly modifying the resulting action when warning is requested if(warn_overwrite) { switch(act_data) { case data_overwrite: case data_overwrite_mark_already_saved: case data_remove: case data_preserve_mark_already_saved: try { string action; switch(act_data) { case data_overwrite: action = gettext("overwritten"); break; case data_overwrite_mark_already_saved: case data_preserve_mark_already_saved: action = gettext("dropped from the archive and marked as already saved"); break; case data_remove: action = gettext("removed"); break; default: throw SRC_BUG; } dialog->pause(tools_printf(gettext("Data of file %S is about to be %S, proceed?"), &full_name, &action)); } catch(Euser_abort & e) { act_data = data_preserve; } break; case data_preserve: case data_undefined: case data_ask: break; default: throw SRC_BUG; } switch(act_ea) { case EA_overwrite: case EA_clear: case EA_preserve_mark_already_saved: case EA_overwrite_mark_already_saved: case EA_merge_overwrite: try { string action; switch(act_ea) { case EA_overwrite: action = gettext("replaced"); break; case EA_clear: action = gettext("removed from the archive"); break; case EA_preserve_mark_already_saved: case EA_overwrite_mark_already_saved: action = gettext("dropped from the archive and marked as already saved"); break; case EA_merge_overwrite: action = gettext("merged with possible overwriting"); break; default: throw SRC_BUG; } dialog->pause(tools_printf(gettext("EA and FSA of file %S are about to be %S, proceed?"), &full_name, &action)); } catch(Euser_abort & e) { act_data = data_preserve; } break; case EA_preserve: case EA_merge_preserve: case EA_ask: case EA_undefined: break; default: throw SRC_BUG; } } switch(act_data) { /////////////////////////// FIRST ACTIONS CATEGORY for DATA ///// case data_preserve: case data_preserve_mark_already_saved: case data_undefined: // remaining data_undefined at the end of the evaluation defaults to data_preserve // drop data if necessary if(act_data == data_preserve_mark_already_saved && al_ino != nullptr) { cat_inode *tmp = const_cast(al_ino); if(tmp->get_saved_status() == saved_status::saved || tmp->get_saved_status() == saved_status::inode_only) tmp->set_saved_status(saved_status::not_saved); // dropping data } // EA consideration if(act_ea == EA_ask) { if(dolly_ino != nullptr && al_ino != nullptr && (dolly_ino->ea_get_saved_status() != ea_saved_status::none || al_ino->ea_get_saved_status() != ea_saved_status::none || dolly_ino->fsa_get_saved_status() != fsa_saved_status::none || al_ino->fsa_get_saved_status() != fsa_saved_status::none) ) act_ea = op_tools_crit_ask_user_for_EA_action(*dialog, full_name, already_here, dolly); else 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. } switch(act_ea) { case EA_preserve: case EA_undefined: // remaining ea_undefined at the end of the evaluation defaults to ea_preserve // nothing to do break; case EA_overwrite: case EA_overwrite_mark_already_saved: case EA_merge_preserve: case EA_merge_overwrite: if(display_treated) dialog->message(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)); do_EFSA_transfert(dialog, act_ea, const_cast(al_ino), dolly_ino); break; case EA_preserve_mark_already_saved: if(al_ino != nullptr && al_ino->ea_get_saved_status() == ea_saved_status::full) { const_cast(al_ino)->ea_set_saved_status(ea_saved_status::partial); if(display_treated) dialog->message(tools_printf(gettext("EA of file %S from first archive have been dropped and marked as already saved"), &full_name)); } if(al_ino != nullptr && al_ino->fsa_get_saved_status() == fsa_saved_status::full) { const_cast(al_ino)->fsa_set_saved_status(fsa_saved_status::partial); if(display_treated) dialog->message(tools_printf(gettext("FSA of file %S from first archive have been dropped and marked as already saved"), &full_name)); } break; case EA_clear: if(al_ino != nullptr && al_ino->ea_get_saved_status() != ea_saved_status::none) { if(al_ino->ea_get_saved_status() == ea_saved_status::full) st.decr_ea_treated(); if(display_treated) dialog->message(tools_printf(gettext("EA of file %S from first archive have been removed"), &full_name)); const_cast(al_ino)->ea_set_saved_status(ea_saved_status::none); } if(al_ino != nullptr && al_ino->fsa_get_saved_status() != fsa_saved_status::none) { if(al_ino->fsa_get_saved_status() == fsa_saved_status::full) st.decr_fsa_treated(); if(display_treated) dialog->message(tools_printf(gettext("FSA of file %S from first archive have been removed"), &full_name)); const_cast(al_ino)->fsa_set_saved_status(fsa_saved_status::none); } break; default: throw SRC_BUG; } // we must keep the existing entry in the catalogue if(display_skipped && (dolly_dir == nullptr || al_dir == nullptr)) dialog->message(tools_printf(gettext("Data of file %S from first archive has been preserved from overwriting"), &full_name)); if(al_dir != nullptr && dolly_dir != nullptr) { // we can recurse in the directory in both ref and current catalogue because both entries are directories try { cat.re_add_in(al_dir->get_name()); // trying to update the add cursor of cat } catch(Erange & e) { ref_tab[index]->skip_read_to_parent_dir(); // updates back the read cursor of catalogue of reference juillet.enfile(&tmp_eod); // updates back the read cursor of juillet cat.skip_read_to_parent_dir(); // updates back the read cursor of cat throw; } } else // there is not the possibility to recurse in directory in both reference catalogue and catalogue under construction { if(al_dir != nullptr) cat.skip_read_to_parent_dir(); if(dolly_dir != nullptr) { ref_tab[index]->skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } } // we may need to clean the corres_copy map if(dolly_mir != nullptr) clean_hard_link_base_from(dolly_mir, corres_copy); // now we can safely destroy the clone object delete dolly; dolly = nullptr; break; /////////////////////////// SECOND ACTIONS CATEGORY for DATA ///// case data_overwrite: case data_overwrite_mark_already_saved: case data_remove: // drop data if necessary if(display_treated) { switch(act_data) { case data_remove: dialog->message(tools_printf(gettext("Data of file %S taken from the first archive of reference has been removed"), &full_name)); break; default: dialog->message(tools_printf(gettext("Data of file %S taken from the first archive of reference has been overwritten"), &full_name)); } } if(act_data == data_overwrite_mark_already_saved && dolly_ino != nullptr) { if(dolly_ino->get_saved_status() == saved_status::saved || dolly_ino->get_saved_status() == saved_status::inode_only) dolly_ino->set_saved_status(saved_status::not_saved); // dropping data } // EA consideration if(act_ea == EA_ask && act_data != data_remove) { if(dolly_ino != nullptr && al_ino != nullptr && (dolly_ino->ea_get_saved_status() != ea_saved_status::none || al_ino->ea_get_saved_status() != ea_saved_status::none || dolly_ino->fsa_get_saved_status() != fsa_saved_status::none || al_ino->fsa_get_saved_status() != fsa_saved_status::none)) act_ea = op_tools_crit_ask_user_for_EA_action(*dialog, full_name, already_here, dolly); else act_ea = EA_overwrite; // no need to ask here neither as both entries have no EA. } if(act_data != data_remove) { switch(act_ea) { case EA_preserve: case EA_undefined: // remaining ea_undefined at the end of the evaluation defaults to ea_preserve do_EFSA_transfert(dialog, EA_overwrite, dolly_ino, al_ino); break; case EA_overwrite: if(display_treated) dialog->message(tools_printf(gettext("EA of file %S has been overwritten"), &full_name)); break; // nothing to do case EA_overwrite_mark_already_saved: if(display_treated) dialog->message(tools_printf(gettext("EA of file %S has been overwritten and marked as already saved"), &full_name)); if(dolly_ino != nullptr && dolly_ino->ea_get_saved_status() == ea_saved_status::full) dolly_ino->ea_set_saved_status(ea_saved_status::partial); break; case EA_merge_preserve: if(display_treated) dialog->message(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)); do_EFSA_transfert(dialog, EA_merge_overwrite, dolly_ino, al_ino); break; case EA_merge_overwrite: if(display_treated) dialog->message(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)); do_EFSA_transfert(dialog, EA_merge_preserve, dolly_ino, al_ino); break; case EA_preserve_mark_already_saved: if(display_treated) dialog->message(tools_printf(gettext("EA of file %S has been overwritten and marked as already saved"), &full_name)); do_EFSA_transfert(dialog, EA_overwrite_mark_already_saved, dolly_ino, al_ino); break; case EA_clear: if(al_ino->ea_get_saved_status() != ea_saved_status::none) { if(display_treated) dialog->message(tools_printf(gettext("EA of file %S from first archive have been removed"), &full_name)); dolly_ino->ea_set_saved_status(ea_saved_status::none); } break; default: throw SRC_BUG; } } else // data_remove { // we remove both objects in overwriting conflict: here for now the dolly clone of the to be added if(dolly_mir != nullptr) clean_hard_link_base_from(dolly_mir, corres_copy); if(dolly_dir != nullptr) { juillet.enfile(&tmp_eod); ref_tab[index]->skip_read_to_parent_dir(); dolly_dir = nullptr; } // now we can safely destroy the clone object delete dolly; dolly = nullptr; } // we must remove the existing entry present in the catalogue to make room for the new entry to be added 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 { cat_ignored_dir *if_removed = nullptr; // to known which counter to decrement st.decr_treated(); if(al_ino != nullptr) if(al_ino->ea_get_saved_status() == ea_saved_status::full) st.decr_ea_treated(); // hard link specific actions if(al_mir != nullptr) { // update back hard link counter st.decr_hard_links(); // updating counter from pointed to inode const cat_inode*al_ptr_ino = al_mir->get_inode(); if(al_ptr_ino == nullptr) throw SRC_BUG; else if(al_ptr_ino->ea_get_saved_status() == ea_saved_status::full) st.decr_ea_treated(); // cleaning the corres_copy map from the pointed to cat_etoile object reference if necessary clean_hard_link_base_from(al_mir, corres_copy); } if(al_det != nullptr) st.decr_deleted(); if(al_ign != nullptr || al_igndir != nullptr) st.decr_ignored(); if(act_data == data_remove) st.incr_ignored(); // remove the current entry from the catalogue if(al_dir != nullptr) { infinint tree_size = al_dir->get_tree_size(); map tiquettes; map::iterator ut; st.add_to_ignored(tree_size); st.sub_from_treated(tree_size); st.sub_from_ea_treated(al_dir->get_tree_ea_num()); st.sub_from_hard_links(al_dir->get_tree_mirage_num()); cat.skip_read_to_parent_dir(); // updating corres_copy with hard_link that will be destroyed due to directory deletion tiquettes.clear(); al_dir->get_etiquettes_found_in_tree(tiquettes); ut = tiquettes.begin(); while(ut != tiquettes.end()) { map::iterator it = corres_copy.find(ut->first); if(it == corres_copy.end()) throw SRC_BUG; // unknown etiquettes found in directory tree if(it->second->get_ref_count() < ut->second) throw SRC_BUG; // more reference found in directory tree toward this cat_etoile than // this cat_etoile is aware of ! if(it->second->get_ref_count() == ut->second) // this cat_etoile will disapear because all its reference are located // in the directory tree we are about to remove, we must clean this // entry from corres_copy corres_copy.erase(it); ++ut; } if(make_empty_dir) { if_removed = new (nothrow) cat_ignored_dir(*al_dir); if(if_removed == nullptr) throw Ememory("filtre_merge"); } } try { // we can now remove the entry from the catalogue cat.remove_read_entry(the_name); // now that the ancient directory has been removed we can replace it by an cat_ignored_dir entry if required if(if_removed != nullptr) cat.add(if_removed); } catch(...) { if(if_removed != nullptr) { delete if_removed; if_removed = nullptr; } throw; } } else // both existing and the one to add are directories we can proceed to the merging of their contents { cat.re_add_in_replace(*dolly_dir); delete dolly; dolly = nullptr; } break; default: /////////////////////////// THIRD ACTIONS CATEGORY for DATA ///// throw SRC_BUG; // unknown data action ! } } // end of overwiting considerations else if(index >= 1 && decremental_mode) { unsigned char firm; // this entry only exists in the auxilliary archive of reference, we must thus replace it by a "cat_detruit // because it will have to be removed when restoring the decremental backup. // we may need to clean the corres_copy map if(dolly_mir != nullptr) { clean_hard_link_base_from(dolly_mir, corres_copy); dolly_mir = nullptr; } // then taking care or directory hierarchy if(dolly_dir != nullptr) { juillet.enfile(&tmp_eod); ref_tab[index]->skip_read_to_parent_dir(); dolly_dir = nullptr; } if(dolly != nullptr) { delete dolly; dolly = nullptr; } else throw SRC_BUG; dolly_ino = nullptr; if(e_mir != nullptr) firm = e_mir->get_inode()->signature(); else firm = e->signature(); dolly = new (nothrow) cat_detruit(the_name, firm, ref_tab[index]->get_current_reading_dir().get_last_modif()); if(dolly == nullptr) throw Ememory("filtre_merge"); dolly_nom = dynamic_cast(dolly); } if(dolly != nullptr) { const cat_inode *e_ino = dynamic_cast(e); cat.add(dolly); st.incr_treated(); if(e_mir != nullptr) { st.incr_hard_links(); e_ino = e_mir->get_inode(); } if(e_detruit != nullptr) st.incr_deleted(); if(e_ino != nullptr) { if(e_ino->ea_get_saved_status() == ea_saved_status::full) st.incr_ea_treated(); if(e_ino->fsa_get_saved_status() == fsa_saved_status::full) st.incr_fsa_treated(); } if(e_dir != nullptr) // we must chdir also for the *reading* method of this catalogue object { if(!cat.read_if_present(&the_name, already_here)) throw SRC_BUG; } } } catch(...) { if(dolly != nullptr) { delete dolly; dolly = nullptr; } throw; } } else // excluded by filters { if(e_dir != nullptr && make_empty_dir) { cat_ignored_dir *igndir = new (nothrow) cat_ignored_dir(*e_dir); if(igndir == nullptr) throw Ememory("filtre_merge"); else cat.add(igndir); } if(display_skipped) dialog->message(string(gettext(SKIPPED)) + juillet.get_string()); st.incr_ignored(); if(e_dir != nullptr) { ref_tab[index]->skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } } } catch(Ebug & e) { throw; } catch(Euser_abort & e) { dialog->message(juillet.get_string() + gettext(" not merged (user choice)")); if(e_dir != nullptr) { dialog->message(gettext("No file in this directory will be considered for merging.")); ref_tab[index]->skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } st.incr_errored(); } catch(Ethread_cancel & e) { throw; } catch(Escript & e) { throw; } catch(Elimitint & e) { throw; } catch(Egeneric & e) { dialog->message(string(gettext("Error while considering file ")) + juillet.get_string() + " : " + e.get_message()); if(e_dir != nullptr) { dialog->message(string(gettext("Warning! No file in this directory will be considered for merging: ")) + juillet.get_string()); ref_tab[index]->skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } st.incr_errored(); } } else // cat_entree is not a cat_nomme object (this is a "cat_eod") { cat_entree *tmp = e->clone(); try { const cat_nomme *already = nullptr; if(tmp == nullptr) throw Ememory("filtre_merge"); if(dynamic_cast(tmp) == nullptr) throw SRC_BUG; cat.add(tmp); // add cat_eod to catalogue (add cursor) cat.read_if_present(nullptr, already); // equivalent to cat_eod for the reading methods } catch(...) { if(tmp != nullptr) delete tmp; throw; } } } normalize_link_base(corres_copy); index++; // next archive etiquette_offset = corres_copy.size(); } } catch(Ethread_cancel & e) { abort = true; dialog->message(gettext("File selection has been aborted. Now building the resulting archive with the already selected files")); if(e.immediate_cancel()) throw; } } catch(...) { if(decr != nullptr) { delete decr; decr = nullptr; overwrite = nullptr; } throw; } if(decr != nullptr) { delete decr; decr = nullptr; overwrite = nullptr; } } void filtre_merge_step2(const shared_ptr & dialog, const pile_descriptor & pdesc, catalogue & cat, bool info_details, bool display_treated, bool display_treated_only_dir, const mask & compr_mask, const infinint & min_compr_size, bool keep_compressed, const infinint & sparse_file_min_size, bool delta_signature, bool build_delta_sig, const infinint & delta_sig_min_size, const mask & delta_mask, bool & abort, thread_cancellation & thr_cancel, bool repair_mode, const delta_sig_block_size & signature_block_size) { compression stock_algo = pdesc.compr->get_algo(); defile juillet = FAKE_ROOT; const cat_entree *e = nullptr; infinint fake_repeat = 0; if(!dialog) throw SRC_BUG; // dialog points to nothing // STEP 2: // 'cat' is now completed // we must copy the file's data, delta sig, EA and FSA to the archive if(info_details) dialog->message("Copying filtered files to the resulting archive..."); cat.set_all_mirage_s_inode_wrote_field_to(false); cat.reset_read(); try { thr_cancel.block_delayed_cancellation(true); while(cat.read(e)) { cat_entree *e_var = const_cast(e); cat_nomme *e_nom = dynamic_cast(e_var); cat_inode *e_ino = dynamic_cast(e_var); cat_file *e_file = dynamic_cast(e_var); cat_mirage *e_mir = dynamic_cast(e_var); cat_directory *e_dir = dynamic_cast(e_var); if(e_var == nullptr) throw SRC_BUG; cat_file::get_data_mode keep_mode = keep_compressed ? cat_file::keep_compressed : cat_file::keep_hole; if(e_mir != nullptr) if(!e_mir->is_inode_wrote()) // we store only once the inode pointed by a set of hard links { e_ino = e_mir->get_inode(); e_file = dynamic_cast(e_ino); if(repair_mode) e_mir->disable_reduction_to_normal_inode(); } juillet.enfile(e); thr_cancel.check_self_cancellation(); if(display_treated_only_dir) { if(e_dir != nullptr) dialog->message(string(gettext("Inspecting directory ")) + juillet.get_string()); } if(e_ino != nullptr) // inode { bool compute_file_crc = false; if(e_file != nullptr && !repair_mode) { const crc *val = nullptr; if(!e_file->get_crc(val)) // this can occur when reading an old archive format compute_file_crc = true; } // deciding whether the file will be compressed or not if(e_file != nullptr) { if(keep_mode != cat_file::keep_compressed) if(compr_mask.is_covered(e_nom->get_name()) && e_file->get_size() >= min_compr_size) e_file->change_compression_algo_write(stock_algo); else e_file->change_compression_algo_write(compression::none); else // keep compressed: e_file->change_compression_algo_write(e_file->get_compression_algo_read()); } // deciding whether the sparse file detection mechanism will be enabled or not // the sparse file detection mechanism is faked active in keep_compressed mode // because we need to record that sparse file datastructure is used as compressed data if(e_file != nullptr) { if(!sparse_file_min_size.is_zero() && keep_mode != cat_file::keep_compressed) // sparse_file detection is activated { if(e_file->get_size() > sparse_file_min_size) { e_file->set_sparse_file_detection_write(true); keep_mode = cat_file::normal; } else if(e_file->get_sparse_file_detection_read()) { e_file->set_sparse_file_detection_write(false); keep_mode = cat_file::normal; } } else // sparse file layer or absence of layer is unchanged e_file->set_sparse_file_detection_write(e_file->get_sparse_file_detection_read()); } // deciding whether we calculate (not just transfer) delta signature bool calculate_delta_signature = false; U_I sig_bl = 0; // stores the signature block len if(e_file != nullptr) { if(!delta_signature) // instructed to remove all delta signature information { if(e_file->has_delta_signature_available()) e_file->clear_delta_signature_only(); } else // delta signature asked for transfer or calculation { if(build_delta_sig) // delta signature need to be recalculated if absent { if(delta_mask.is_covered(juillet.get_string()) && e_file->get_size() >= delta_sig_min_size) { if(!e_file->has_delta_signature_available()) { switch(keep_mode) { case cat_file::keep_compressed: // reading a compressed file and keep compressed mode asked, // situation that should be filtered out by sanity checks throw SRC_BUG; case cat_file::keep_hole: if(e_file->get_sparse_file_detection_read()) { string tmp = juillet.get_string(); dialog->message(tools_printf(gettext("Need to activate sparse file detection in order to calculate delta signature for sparse file %S"), &tmp)); } else { e_file->will_have_delta_signature_available(); calculate_delta_signature = true; sig_bl = signature_block_size.calculate(e_file->get_size()); } break; case cat_file::normal: case cat_file::plain: e_file->will_have_delta_signature_available(); calculate_delta_signature = true; sig_bl = signature_block_size.calculate(e_file->get_size()); break; default: throw SRC_BUG; } } } else { if(e_file->has_delta_signature_available()) e_file->clear_delta_signature_only(); } } // else nothing to do, save_inode will transfer existing delta signatures } } // saving inode's data if(!save_inode(dialog, juillet.get_string(), e_var, nullptr, // ref pdesc, info_details, display_treated, true, // alter_atime false, // check_change compute_file_crc, keep_mode, cat, 0, // repeat_count 0, // repeat_byte sparse_file_min_size, nullptr, // semaphore calculate_delta_signature, // delta_signature false, // delta_diff fake_repeat, // current_wasted_bytes set(), // empty list for ignored_as_symlink repair_mode, sig_bl)) throw SRC_BUG; else // succeeded saving { if(e_mir != nullptr) e_mir->set_inode_wrote(true); } // saving inode's EA if(e_ino->ea_get_saved_status() == ea_saved_status::full) { cat.pre_add_ea(e, &pdesc); // ignoring the return value of save_ea, exceptions may still propagate (void)save_ea(dialog, juillet.get_string(), e_ino, pdesc, display_treated, repair_mode); cat.pre_add_ea_crc(e,&pdesc); } // saving inode's FSA if(e_ino->fsa_get_saved_status() == fsa_saved_status::full) { cat.pre_add_fsa(e, &pdesc); // ignoring the return value of save_fsa, exceptions may still propagate (void)save_fsa(dialog, juillet.get_string(), e_ino, pdesc, display_treated, repair_mode); cat.pre_add_fsa_crc(e, &pdesc); } } else // not an inode { cat.pre_add(e, &pdesc); if(e_mir != nullptr && (e_mir->get_inode()->get_saved_status() == saved_status::saved || e_mir->get_inode()->ea_get_saved_status() == ea_saved_status::full)) if(display_treated) dialog->message(string(gettext("Adding Hard link to archive: "))+juillet.get_string()); } // we can now check for interrution requests thr_cancel.block_delayed_cancellation(false); thr_cancel.block_delayed_cancellation(true); } } catch(Egeneric & e) { Ethread_cancel* e_thread = dynamic_cast(&e); Erange* e_range = dynamic_cast(&e); if(info_details) { string msg = e.get_message(); dialog->message(tools_printf(gettext("error met while creating archive: %S"), & msg)); } if(e_thread == nullptr && e_range == nullptr) throw; if(!repair_mode) cat.tail_catalogue_to_current_read(); cat.change_location(pdesc); if(pdesc.compr->is_compression_suspended()) { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->resume_compression(); } thr_cancel.block_delayed_cancellation(false); throw; } cat.change_location(pdesc); if(pdesc.compr->is_compression_suspended()) { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->resume_compression(); } thr_cancel.block_delayed_cancellation(false); if(abort) throw Ethread_cancel(false, 0); } void filtre_sequentially_read_all_catalogue(catalogue & cat, const shared_ptr & dialog, bool lax_read_mode) { const cat_entree *e; thread_cancellation thr_cancel; defile juillet = FAKE_ROOT; cat.set_all_mirage_s_inode_wrote_field_to(false); cat.reset_read(); try { while(cat.read(e)) { const cat_file *e_file = dynamic_cast(e); const cat_inode *e_ino = dynamic_cast(e); const cat_mirage *e_mir = dynamic_cast(e); const crc *check = nullptr; juillet.enfile(e); thr_cancel.check_self_cancellation(); if(e_mir != nullptr) { if(!e_mir->is_inode_wrote()) { e_file = dynamic_cast(e_mir->get_inode()); e_ino = e_mir->get_inode(); } } try { if(e_file != nullptr) (void)e_file->get_crc(check); } catch(Erange & e) { string msg = string(gettext("failed reading CRC from file: ")) + juillet.get_string(); if(lax_read_mode) dialog->message(msg); else throw Edata(msg); } if(e_mir != nullptr && (e_ino != nullptr || e_file != nullptr)) e_mir->set_inode_wrote(true); try { if(e_ino != nullptr) { if(e_ino->ea_get_saved_status() == ea_saved_status::full) { (void)e_ino->get_ea(); e_ino->ea_get_crc(check); } if(e_ino->fsa_get_saved_status() == fsa_saved_status::full) { (void)e_ino->get_fsa(); e_ino->fsa_get_crc(check); } } } catch(Erange & e) { string msg = string(gettext("Failed reading CRC for EA and FSA: ")) + juillet.get_string(); if(lax_read_mode) dialog->message(msg); else throw Edata(msg); } } } catch(Erange & e) { dialog->message(string(gettext("Error met while reading next entry: ")) + juillet.get_string()); } } static bool save_inode(const shared_ptr & dialog, const string & info_quoi, cat_entree * & e, const cat_entree * ref, 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, bool delta_signature, bool delta_diff, infinint & current_wasted_bytes, set ignored_as_symlink, bool repair_mode, U_I signature_block_size) { bool ret = true; infinint current_repeat_count = 0; infinint storage_size; bool loop; cat_mirage *mir = dynamic_cast(e); cat_inode *ino = dynamic_cast(e); cat_file *fic = dynamic_cast(ino); const cat_file *ref_fic = dynamic_cast(ref); bool resave_uncompressed = false; infinint rewinder; // we skip back here if data must be saved uncompressed shared_ptr delta_sig_ref; // holds the delta_signature that will be used as reference for delta patch later on U_I sig_ref_block_len; const crc *result_crc = nullptr; if(pdesc.compr == nullptr) throw SRC_BUG; // recording the position where to skip back upon // poor compression or if file changed while we were // saving it: pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->sync_write(); rewinder = pdesc.stack->get_position(); if(mir != nullptr) { ino = mir->get_inode(); if(ino == nullptr) throw SRC_BUG; fic = dynamic_cast(ino); } try // to be able to release ref_fic's delta signature in any case { if(fic != nullptr && fic->get_saved_status() == saved_status::delta) { if(delta_diff) // not a merging operation { if(ref_fic == nullptr || !ref_fic->has_delta_signature_available()) throw SRC_BUG; // fetching the delta signature to base the patch on ref_fic->read_delta_signature(delta_sig_ref, sig_ref_block_len); if(!delta_sig_ref) throw SRC_BUG; // need to store at least the base CRC and result CRC even if not delta signature is computed: fic->will_have_delta_signature_structure(); if(ref_fic->has_patch_result_crc()) { const crc *tmp = nullptr; ref_fic->get_patch_result_crc(tmp); if(tmp == nullptr) throw SRC_BUG; fic->set_patch_base_crc(*tmp); } else throw SRC_BUG; } } do // loop if resave_uncompressed is set, this is the OUTER LOOP { // PRE RECORDING THE INODE (for sequential reading) cat.pre_add(e, &pdesc); // EXITING FOR NON INODE ENTRIES if(ino == nullptr) break; // exit the OUTLER LOOP // WRITING DOWN DELTA SIG FOR NO SAVED FILES if(fic != nullptr && fic->has_delta_signature_structure() && fic->get_saved_status() != saved_status::saved && fic->get_saved_status() != saved_status::delta) { save_delta_signature(dialog, info_quoi, fic, ref_fic, pdesc, signature_block_size, info_details, display_treated, cat); } // EXITING FOR INODE ENTRY WITHOUT DATA TO SAVE if(ino->get_saved_status() != saved_status::saved && ino->get_saved_status() != saved_status::delta) { if(sem != nullptr) sem->raise(info_quoi, ino, false); if(ino->get_saved_status() == saved_status::inode_only && display_treated) dialog->message(tools_printf(gettext("Adding only inode metadata to archive: %S"), &info_quoi)); break; // exiting the OUTER LOOP } if(compute_crc && (keep_mode != cat_file::normal && keep_mode != cat_file::plain)) throw SRC_BUG; // cannot compute crc if data is compressed or hole datastructure not interpreted // TREATING INODE THAT NEED DATA SAVING if(display_treated) { if(resave_uncompressed) dialog->message(string(gettext("Resaving file without compression: ")) + info_quoi); else { if(delta_sig_ref) dialog->message(string(gettext("Delta saving file to archive: ")) + info_quoi); else { string i_type = ino->get_description(); dialog->message(tools_printf(gettext("Adding %S to archive: %S"), &i_type, &info_quoi)); } } } if(fic != nullptr) { shared_ptr delta_sig; if(sem != nullptr) sem->raise(info_quoi, ino, true); try // protecting delta_sig and putting "sem" in proper status in any case { do // while "loop" is true, this is the INNER LOOP { loop = false; infinint start; generic_file *source = nullptr; ////////////////////////////// // preparing the source try { switch(keep_mode) { case cat_file::keep_compressed: if(fic->get_compression_algo_read() != fic->get_compression_algo_write()) keep_mode = cat_file::keep_hole; source = fic->get_data(keep_mode, nullptr, signature_block_size, nullptr); break; case cat_file::keep_hole: if(delta_signature && !fic->get_sparse_file_detection_read()) { delta_sig.reset(new (nothrow) memory_file()); if(!delta_sig) throw Ememory("saved_inode"); source = fic->get_data(cat_file::normal, delta_sig, signature_block_size, shared_ptr()); if(display_treated) dialog->message(tools_printf(gettext("building delta signature with block size of %d bytes"), signature_block_size)); } else source = fic->get_data(keep_mode, nullptr, signature_block_size, nullptr); break; case cat_file::normal: if(delta_signature) { delta_sig.reset(new (nothrow) memory_file()); if(!delta_sig) throw Ememory("save_inode"); if(display_treated) dialog->message(tools_printf(gettext("building delta signature with block size of %d bytes"), signature_block_size)); } if(fic->get_sparse_file_detection_read()) // source file already holds a sparse_file structure source = fic->get_data(cat_file::plain, delta_sig, signature_block_size, delta_sig_ref, & result_crc); // we must hide the holes for it can be redetected else source = fic->get_data(cat_file::normal, delta_sig, signature_block_size, delta_sig_ref, & result_crc); break; case cat_file::plain: throw SRC_BUG; // save_inode must never be called with this value default: throw SRC_BUG; // unknown value for keep_mode } } catch(...) { cat.pre_add_failed_mark(&pdesc); throw; } ////////////////////////////// // preparing the destination if(source != nullptr) { try { sparse_file *dst_hole = nullptr; generic_rsync *dst_rsync = nullptr; infinint crc_size = tools_file_size_to_crc_size(fic->get_size()); crc * val = nullptr; const crc * original = nullptr; bool crc_available = false; bool set_storage_size_to_zero = false; // only used in repair mode in case of missing CRC and impossibility to skip backward source->skip(0); source->read_ahead(0); pdesc.stack->sync_write_above(pdesc.compr); 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 if(keep_mode == cat_file::keep_compressed || fic->get_compression_algo_write() == compression::none) pdesc.compr->suspend_compression(); else pdesc.compr->resume_compression(); // now that compression has reset we can fetch the location where the data will be dropped: start = pdesc.stack->get_position(); try { if(fic->get_sparse_file_detection_write() && keep_mode != cat_file::keep_compressed && keep_mode != cat_file::keep_hole && !delta_diff) { // creating the sparse_file to copy data to destination dst_hole = new (nothrow) sparse_file(pdesc.stack->top(), hole_size); if(dst_hole == nullptr) throw Ememory("save_inode"); pdesc.stack->push(dst_hole); } try { ////////////////////////////// // proceeding to file's data backup source->copy_to(*pdesc.stack, crc_size, val); if(val == nullptr) throw SRC_BUG; // crc must be read after the data has been copied // case of repairing, we use sequential reading if(!compute_crc) crc_available = fic->get_crc(original); else crc_available = false; } catch(...) { bool fixed = false; bool try_skip = false; if(!repair_mode) throw; dialog->printf(gettext("Failed reading data CRC for %S, file may be damaged and will be marked dirty"), &info_quoi); fic->set_dirty(true); // in the following we try to skip backward to set the storage size to zero // in both sequential data and ending generated catalog if(pdesc.stack->truncatable(start)) { try { pdesc.stack->truncate(start); fixed = true; } catch(Ebug & e) { throw; } catch(Egeneric & e) { try_skip = true; } } else try_skip = true; if(try_skip) { infinint current_pos = pdesc.stack->get_position(); if(pdesc.stack->skippable(generic_file::skip_backward, current_pos - start)) { if(!pdesc.stack->skip(start)) pdesc.stack->skip(current_pos); else fixed = true; } } val->clear(); fic->set_crc(*val); if(!fixed) { dialog->printf(gettext("Failed setting storage size to zero for this file with missing data CRC, CRC error will be reported for that file while reading the repaired archive")); set_storage_size_to_zero = true; } if(fic->has_delta_signature_structure()) fic->clear_delta_signature_only(); } ////////////////////////////// // checking crc value and storing it in catalogue if(compute_crc) fic->set_crc(*val); else { if(keep_mode == cat_file::normal && crc_available && !fic->get_sparse_file_detection_read()) // crc is calculated based on the stored uncompressed data // we can compare only if the data is uncompressed (not // keep_compressed and hole structure inlined in data // is kept as is (not in nomal mode when the source has // a sparse_file layer { if(original == nullptr) throw SRC_BUG; if(typeid(*original) != typeid(*val)) throw SRC_BUG; if(*original != *val) throw Erange("save_inode", gettext("Copied data does not match CRC")); } } ////////////////////////////// // checking whether saved files used sparse_file datastructure if(dst_hole != nullptr) { pdesc.stack->sync_write_above(dst_hole); dst_hole->sync_write(); if(!dst_hole->has_seen_hole() && !dst_hole->has_escaped_data()) fic->set_sparse_file_detection_write(false); // here we drop the sparse_file datastructure as no hole // could be read. This will speed up extraction when used // normally (not with sequential reading, as the inode info // is already written to file and cannot be changed. // Reading as sparse_file datastructure a plain normal data // is possible while there is no data to escape, this is just // a bit more slower.). } fichier_global *s_fic = dynamic_cast(source); try { if(s_fic != nullptr) s_fic->fadvise(fichier_global::advise_dontneed); } catch(Erange & e) { dialog->message(tools_printf(gettext("Failed to set fadvise() for file %S, this is not crucial, no problem"), &info_quoi)); } source->terminate(); // we need this for amount of data written to be properly calculated pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->sync_write(); } catch(...) { if(val != nullptr) { delete val; val = nullptr; } if(dst_rsync != nullptr) { if(pdesc.stack->pop() != dst_rsync) throw SRC_BUG; delete dst_rsync; dst_rsync = nullptr; } if(dst_hole != nullptr) { if(pdesc.stack->pop() != dst_hole) throw SRC_BUG; delete dst_hole; dst_hole = nullptr; } throw; } if(val != nullptr) { delete val; val = nullptr; } if(dst_rsync != nullptr) { if(pdesc.stack->pop() != dst_rsync) throw SRC_BUG; delete dst_rsync; dst_rsync = nullptr; } if(dst_hole != nullptr) { dst_hole->terminate(); if(pdesc.stack->pop() != dst_hole) throw SRC_BUG; delete dst_hole; dst_hole = nullptr; } if(pdesc.stack->get_position() >= start || set_storage_size_to_zero) { if(set_storage_size_to_zero) storage_size = 0; else storage_size = pdesc.stack->get_position() - start; } else throw SRC_BUG; } catch(...) { delete source; source = nullptr; // restore atime of source if(!alter_atime) tools_noexcept_make_date(info_quoi, false, ino->get_last_access(), ino->get_last_modif(), ino->get_last_modif()); throw; } delete source; source = nullptr; ////////////////////////////// // adding the data CRC if escape marks are used cat.pre_add_crc(ino, &pdesc); ////////////////////////////// // checking if compressed data is smaller than uncompressed one if(fic->get_size() <= storage_size && keep_mode != cat_file::keep_compressed && fic->get_compression_algo_write() != compression::none && !repair_mode) { infinint current_pos_tmp = pdesc.stack->get_position(); bool try_skip = false; if(current_pos_tmp < rewinder) throw SRC_BUG; // we are positionned before the start of the current inode dump! if(pdesc.stack->truncatable(rewinder)) { try { pdesc.stack->truncate(rewinder); if(!resave_uncompressed) resave_uncompressed = true; else throw SRC_BUG; // should only be tried once per inode fic->change_compression_algo_write(compression::none); break; // stop the inner loop } catch(Ebug & e) { throw; } catch(...) { try_skip = true; } } else try_skip = true; if(try_skip && pdesc.stack->skippable(generic_file::skip_backward, current_pos_tmp - rewinder)) { try { if(!pdesc.stack->skip(rewinder)) throw Erange("save_inode","skipping was possible in theory but not in reality"); // this situation may arise when some data // is pending to be written (cache layer) and // before skipping back leads the cache to flush // his data to the underlying layer (sar for example) // which may leads to the creation to a new slice and // then skipping back is no more possible as it was before if(!resave_uncompressed) resave_uncompressed = true; else throw SRC_BUG; // should only be tried once per inode fic->change_compression_algo_write(compression::none); break; // stop the inner loop } catch(Ebug & e) { throw; } catch(...) { if(info_details) dialog->message(info_quoi + gettext(" : Failed resaving uncompressed the inode data")); // ignoring the error and continuing resave_uncompressed = false; if(pdesc.stack->get_position() != current_pos_tmp) throw SRC_BUG; } } else { if(info_details) dialog->message(info_quoi + gettext(" : Resaving uncompressed the inode data to gain space is not possible, keeping data compressed")); } } else resave_uncompressed = false; if(!resave_uncompressed) { // in repair_mode, the file offset points to the data // in the damaged archive, it must not be set before the // data is read at which time it records the offset in the // repaired archive, the offset may change espetially when // encryption is used, due to variable sized elastic buffers // // another situation is while merging an recompressing // data, if the new compression gives less good result than // the actual, resave_uncompressed will be tried and // need to set back the source data (fic->get_offset()) // to the position in the source archive so we must // not overwrite this value until we know we won't need it // anymore fic->set_offset(start); fic->set_storage_size(storage_size); } ////////////////////////////// // checking for last_modification date change if(check_change) { bool changed = false; try { changed = (fic->get_last_modif() != tools_get_mtime(*dialog, info_quoi, true, true, // silently set to zero negative dates ignored_as_symlink)); } catch(Erange & e) { dialog->message(tools_printf(gettext("File has disappeared while we were reading it, cannot check whether it has changed during its backup: %S"), &info_quoi)); changed = false; } if(changed) { if(current_repeat_count < repeat_count) { current_repeat_count++; infinint current_pos_tmp = pdesc.stack->get_position(); try { bool try_skip = false; if(pdesc.stack->truncatable(start)) { try { pdesc.stack->truncate(start); } catch(Ebug & e) { throw; } catch(...) { try_skip = true; } } else try_skip = true; if(try_skip) { if(pdesc.stack->skippable(generic_file::skip_backward, storage_size)) { if(!pdesc.stack->skip(start)) { if(!pdesc.stack->skip(start)) throw SRC_BUG; throw Erange("",""); // used locally } } else throw Erange("",""); // used locally, not propagated over this try / catch block } } catch(...) { current_wasted_bytes += current_pos_tmp - start; if(pdesc.stack->get_position() != current_pos_tmp) throw SRC_BUG; } if(repeat_byte.is_zero() || (current_wasted_bytes < repeat_byte)) { if(info_details) dialog->message(tools_printf(gettext("WARNING! File modified while reading it for backup. Performing retry %i of %i"), ¤t_repeat_count, &repeat_count)); if(pdesc.stack->get_position() != start) cat.pre_add_waste_mark(&pdesc); loop = true; // updating the last modification date of file fic->set_last_modif(tools_get_mtime(*dialog, info_quoi, true, true, // silently set to zero negative date ignored_as_symlink)); // updating the size of the file fic->change_size(tools_get_size(info_quoi)); } else { dialog->message(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); fic->set_dirty(true); ret = false; } } else { dialog->message(string(gettext("WARNING! File modified while reading it for backup, but no more retry allowed: ")) + info_quoi); fic->set_dirty(true); cat.pre_add_dirty(&pdesc); // when in sequential reading ret = false; } } } ////////////////////////////// // restore atime of source if(!alter_atime) tools_noexcept_make_date(info_quoi, false, ino->get_last_access(), ino->get_last_modif(), ino->get_last_modif()); ////////////////////////////// // dumping delta signature if present or just calculated if(fic->has_delta_signature_structure() && !loop) { U_I block_size = signature_block_size; // by default signature has been calculated with this given block size if(display_treated) dialog->message(string(gettext("Dumping delta signature structure for saved file: ")) + info_quoi); if(!delta_sig) // no delta_sig got calculated during this save_inode() execution { if(!delta_diff) { // merging context, signature not calculated here but already existing: we need to transfer it if(fic->has_delta_signature_available() || repair_mode) fic->read_delta_signature(delta_sig, block_size); // overriden block_size by the value of the signature // we have just read. As we won't recalculate it, in the present // case, we have to properly record its block size } // else delta diff without delta signature, storing en empty zero length signature // delta_sig stays equal to nullptr } if(!fic->has_patch_result_crc()) { const crc *tmp = nullptr; switch(fic->get_saved_status()) { case saved_status::saved: fic->get_crc(tmp); break; case saved_status::fake: throw SRC_BUG; case saved_status::not_saved: if(ref_fic == nullptr) throw SRC_BUG; if(!ref_fic->has_patch_result_crc()) throw SRC_BUG; ref_fic->get_patch_result_crc(tmp); break; case saved_status::delta: if(result_crc == nullptr) throw SRC_BUG; tmp = result_crc; break; default: throw SRC_BUG; } if(tmp == nullptr) throw SRC_BUG; fic->set_patch_result_crc(*tmp); } // adding a tape mark when in sequential read mode cat.pre_add_delta_sig(&pdesc); pdesc.compr->suspend_compression(); // dropping the data to the archive and recording its location in the cat_file object try { if(delta_sig) fic->dump_delta_signature(delta_sig, block_size, *(pdesc.compr), pdesc.esc != nullptr); else fic->dump_delta_signature(*(pdesc.compr), pdesc.esc != nullptr); } catch(...) { fic->drop_delta_signature_data(); throw; } fic->drop_delta_signature_data(); } } else throw SRC_BUG; // saved_status == saved_status::saved, but no data available, and no exception raised; } while(loop); // INNER LOOP } catch(...) { if(sem != nullptr) sem->lower(); fic->drop_delta_signature_data(); // no more needed delta signature data if present throw; } if(sem != nullptr) sem->lower(); fic->drop_delta_signature_data(); // no more needed delta signature data if present } else // fic == nullptr if(sem != nullptr) { sem->raise(info_quoi, ino, true); sem->lower(); } } while(resave_uncompressed); // OUTER LOOP } catch(...) { if(ref_fic != nullptr) ref_fic->drop_delta_signature_data(); throw; } if(ref_fic != nullptr) ref_fic->drop_delta_signature_data(); return ret; } static bool save_ea(const shared_ptr & dialog, const string & info_quoi, cat_inode * & ino, const pile_descriptor & pdesc, bool display_treated, bool repair_mode) { bool ret = false; try { switch(ino->ea_get_saved_status()) { case ea_saved_status::full: // if there is something to save if(ino->get_ea() != nullptr) { crc * val = nullptr; infinint start; try { if(display_treated) dialog->message(string(gettext("Saving Extended Attributes for ")) + info_quoi); if(pdesc.compr->is_compression_suspended()) { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->resume_compression(); // always compress EA (no size or filename consideration) } else { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->sync_write(); // reset the compression engine to be able to decompress from that point later } start = pdesc.stack->get_position(); pdesc.stack->reset_crc(tools_file_size_to_crc_size(ino->ea_get_size())); // start computing CRC for any read/write on stack try { ino->get_ea()->dump(*pdesc.stack); } catch(...) { val = pdesc.stack->get_crc(); // this keeps "stack" in a coherent status throw; } ino->ea_set_offset(start); val = pdesc.stack->get_crc(); if(repair_mode) { const crc *tmp = nullptr; ino->ea_get_crc(tmp); if(tmp == nullptr) throw SRC_BUG; if(*tmp != *val) { dialog->printf(gettext("Computed EA CRC for file %S differs from what was stored in the archive, this file's EA may have been corrupted"), &info_quoi); ino->ea_set_crc(*val); } } else ino->ea_set_crc(*val); ino->ea_detach(); ret = true; } catch(...) { if(val != nullptr) delete val; throw; } if(val != nullptr) delete val; } else throw SRC_BUG; break; case ea_saved_status::partial: case ea_saved_status::none: break; case ea_saved_status::fake: throw SRC_BUG; //filesystem, must not provide inode in such a status case ea_saved_status::removed: throw SRC_BUG; //filesystem, must not provide inode in such a status default: throw SRC_BUG; } } catch(Ebug & e) { throw; } catch(Euser_abort & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { dialog->message(string(gettext("Error saving Extended Attributes for ")) + info_quoi + ": " + e.get_message()); if(repair_mode) { ino->ea_set_saved_status(ea_saved_status::none); dialog->message(gettext("be advised that a CRC error will be reported for the EA of that file while sequentially reading the repaired archive")); } } return ret; } static void restore_atime(const string & chemin, const cat_inode * & ptr) { const cat_file * ptr_f = dynamic_cast(ptr); if(ptr_f != nullptr) tools_noexcept_make_date(chemin, false, ptr_f->get_last_access(), ptr_f->get_last_modif(), ptr_f->get_last_modif()); } static bool save_fsa(const shared_ptr & dialog, const string & info_quoi, cat_inode * & ino, const pile_descriptor & pdesc, bool display_treated, bool repair_mode) { bool ret = false; try { switch(ino->fsa_get_saved_status()) { case fsa_saved_status::full: // if there is something to save if(ino->get_fsa() != nullptr) { crc * val = nullptr; infinint start; try { if(display_treated) dialog->message(string(gettext("Saving Filesystem Specific Attributes for ")) + info_quoi); if(pdesc.compr->get_algo() != compression::none) { pdesc.stack->sync_write_above(pdesc.compr); pdesc.compr->suspend_compression(); // never compress FSA (no size or filename consideration) } start = pdesc.stack->get_position(); pdesc.stack->reset_crc(tools_file_size_to_crc_size(ino->fsa_get_size())); // start computing CRC for any read/write on stack try { ino->get_fsa()->write(*pdesc.stack); } catch(...) { val = pdesc.stack->get_crc(); // this keeps "" in a coherent status ino->fsa_set_crc(*val); ino->fsa_set_offset(start); // and we also keep ino in a coherent status throw; } ino->fsa_set_offset(start); val = pdesc.stack->get_crc(); if(repair_mode) { const crc *tmp = nullptr; ino->fsa_get_crc(tmp); if(tmp == nullptr) throw SRC_BUG; if(*tmp != *val) { dialog->printf(gettext("Computed FSA CRC for file %S differs from what was stored in the archive, this file's EA may have been corrupted"), &info_quoi); ino->fsa_set_crc(*val); } } else ino->fsa_set_crc(*val); ino->fsa_detach(); ret = true; // compression is left suspended, save_inode, save_ea, will change or catalogue dump will change it if necessary } catch(...) { if(val != nullptr) delete val; throw; } if(val != nullptr) delete val; } else throw SRC_BUG; break; case fsa_saved_status::partial: case fsa_saved_status::none: break; default: throw SRC_BUG; } } catch(Ebug & e) { throw; } catch(Euser_abort & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { dialog->message(string(gettext("Error saving Filesystem Specific Attributes for ")) + info_quoi + ": " + e.get_message()); if(repair_mode) { ino->fsa_set_saved_status(fsa_saved_status::none); dialog->message(gettext("be advised that a CRC error will be reported for the FSA of that file while sequentially reading the repaired archive")); } } return ret; } static void do_EFSA_transfert(const shared_ptr &dialog, over_action_ea action, cat_inode *place_ino, const cat_inode *add_ino) { ea_attributs *tmp_ea = nullptr; filesystem_specific_attribute_list *tmp_fsa = nullptr; switch(action) { case EA_overwrite: case EA_overwrite_mark_already_saved: case EA_merge_preserve: case EA_merge_overwrite: break; case EA_preserve: case EA_preserve_mark_already_saved: case EA_clear: throw SRC_BUG; default: throw SRC_BUG; } if(add_ino == nullptr) // to_add is not an inode thus cannot have any EA return; // we do nothing in any case as there is not different EA set in conflict if(place_ino == nullptr) // in_place is not an inode thus cannot have any EA return; // nothing can be done neither here as the resulting object (in_place) cannot handle EA // in the following we know that both in_place and to_add are inode, //we manipulate them thanks to their cat_inode * pointers (place_ino and add_ino) switch(action) // action concerns both EA and FSA in spite of the name of its values { case EA_overwrite: // overwriting last change date if(add_ino->has_last_change()) place_ino->set_last_change(add_ino->get_last_change()); // EA Consideration (for overwriting) switch(add_ino->ea_get_saved_status()) { case ea_saved_status::none: case ea_saved_status::removed: place_ino->ea_set_saved_status(ea_saved_status::none); break; case ea_saved_status::partial: case ea_saved_status::fake: place_ino->ea_set_saved_status(ea_saved_status::partial); break; case ea_saved_status::full: tmp_ea = new (nothrow) ea_attributs(*add_ino->get_ea()); // we clone the EA of add_ino if(tmp_ea == nullptr) throw Ememory("filtre::do_EFSA_transfert"); try { if(place_ino->ea_get_saved_status() == ea_saved_status::full) // then we must drop the old EA: place_ino->ea_detach(); else place_ino->ea_set_saved_status(ea_saved_status::full); place_ino->ea_attach(tmp_ea); tmp_ea = nullptr; } catch(...) { if(tmp_ea != nullptr) { delete tmp_ea; tmp_ea = nullptr; } throw; } break; default: throw SRC_BUG; } // FSA Considerations (for overwriting) switch(add_ino->fsa_get_saved_status()) { case fsa_saved_status::none: place_ino->fsa_set_saved_status(fsa_saved_status::none); break; case fsa_saved_status::partial: place_ino->fsa_set_saved_status(fsa_saved_status::partial); place_ino->fsa_partial_attach(add_ino->fsa_get_families()); break; case fsa_saved_status::full: tmp_fsa = new (nothrow) filesystem_specific_attribute_list(*add_ino->get_fsa()); // we clone the FSA of add_ino if(tmp_fsa == nullptr) throw Ememory("filtre::do_EFSA_transfer"); try { if(place_ino->fsa_get_saved_status() == fsa_saved_status::full) // we must drop the old FSA place_ino->fsa_detach(); else place_ino->fsa_set_saved_status(fsa_saved_status::full); place_ino->fsa_attach(tmp_fsa); tmp_fsa = nullptr; } catch(...) { if(tmp_fsa != nullptr) { delete tmp_fsa; tmp_fsa = nullptr; } throw; } break; default: throw SRC_BUG; } break; // end of case EA_FSA_overwrite for action case EA_overwrite_mark_already_saved: // Overwriting Date if(add_ino->has_last_change()) place_ino->set_last_change(add_ino->get_last_change()); // EA considerations 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. if(place_ino->ea_get_saved_status() == ea_saved_status::full || place_ino->ea_get_saved_status() == ea_saved_status::fake) place_ino->ea_set_saved_status(ea_saved_status::partial); // FSA considerations 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 if(place_ino->fsa_get_saved_status() == fsa_saved_status::full) place_ino->fsa_set_saved_status(fsa_saved_status::partial); break; case EA_merge_preserve: // no last change date modification (preserve context) // EA considerations if(place_ino->ea_get_saved_status() == ea_saved_status::full && add_ino->ea_get_saved_status() == ea_saved_status::full) // we have something to merge { tmp_ea = new (nothrow) ea_attributs(); if(tmp_ea == nullptr) throw Ememory("filtre.cpp:do_EFSA_transfert"); try { merge_ea(*place_ino->get_ea(), *add_ino->get_ea(), *tmp_ea); place_ino->ea_detach(); place_ino->ea_attach(tmp_ea); tmp_ea = nullptr; } catch(...) { if(tmp_ea != nullptr) { delete tmp_ea; tmp_ea = nullptr; } throw; } } else if(add_ino->ea_get_saved_status() == ea_saved_status::full) { place_ino->ea_set_saved_status(ea_saved_status::full); // it was not the case else we would have executed the above block tmp_ea = new (nothrow) ea_attributs(*add_ino->get_ea()); // we clone the EA set of to_add if(tmp_ea == nullptr) throw Ememory("filtre.cpp:do_EFSA_transfert"); try { place_ino->ea_attach(tmp_ea); tmp_ea = nullptr; } catch(...) { if(tmp_ea != nullptr) { delete tmp_ea; tmp_ea = nullptr; } throw; } } // else nothing is done: either res_ino has full EA but ref_ino has not // or res_ino has not full EA nor do has ref_ino and nothing can be done neither // FSA considerations if(place_ino->fsa_get_saved_status() == fsa_saved_status::full && add_ino->fsa_get_saved_status() == fsa_saved_status::full) // we have something to merge { tmp_fsa = new (nothrow) filesystem_specific_attribute_list(); if(tmp_fsa == nullptr) throw Ememory("filtre.cpp::do_EFSA_transfer"); try { *tmp_fsa = *add_ino->get_fsa() + *place_ino->get_fsa(); // overwriting add_ino with place_ino's FSA place_ino->fsa_detach(); place_ino->fsa_attach(tmp_fsa); tmp_fsa = nullptr; } catch(...) { if(tmp_fsa != nullptr) { delete tmp_fsa; tmp_fsa = nullptr; } throw; } } else { if(add_ino->fsa_get_saved_status() == fsa_saved_status::full) { place_ino->fsa_set_saved_status(fsa_saved_status::full); tmp_fsa = new (nothrow) filesystem_specific_attribute_list(*add_ino->get_fsa()); if(tmp_fsa == nullptr) throw Ememory("filtre.cpp:do_EFSA_transfert"); try { place_ino->fsa_attach(tmp_fsa); tmp_fsa = nullptr; } catch(...) { if(tmp_fsa != nullptr) { delete tmp_fsa; tmp_fsa = nullptr; } throw; } } // else nothing to be done (in_place eventually has FSA and add_ino does nothing to add) } break; case EA_merge_overwrite: // last change date transfert if(add_ino->has_last_change()) place_ino->set_last_change(add_ino->get_last_change()); // EA considerations if(place_ino->ea_get_saved_status() == ea_saved_status::full && add_ino->ea_get_saved_status() == ea_saved_status::full) { tmp_ea = new (nothrow) ea_attributs(); if(tmp_ea == nullptr) throw Ememory("filtre.cpp:do_EFSA_transfert"); try { merge_ea(*add_ino->get_ea(), *place_ino->get_ea(), *tmp_ea); place_ino->ea_detach(); place_ino->ea_attach(tmp_ea); tmp_ea = nullptr; } catch(...) { if(tmp_ea != nullptr) { delete tmp_ea; tmp_ea = nullptr; } throw; } } else if(add_ino->ea_get_saved_status() == ea_saved_status::full) { place_ino->ea_set_saved_status(ea_saved_status::full); // it was not the case else we would have executed the above block tmp_ea = new (nothrow) ea_attributs(*add_ino->get_ea()); if(tmp_ea == nullptr) throw Ememory("filtre.cpp:do_EFSA_transfert"); try { place_ino->ea_attach(tmp_ea); tmp_ea = nullptr; } catch(...) { if(tmp_ea != nullptr) { delete tmp_ea; tmp_ea = nullptr; } throw; } } // else nothing is done: either res_ino has full EA but ref_ino has not // or res_ino has not full EA nor do has ref_ino and nothing can be done neither // FSA considerations if(place_ino->fsa_get_saved_status() == fsa_saved_status::full && add_ino->fsa_get_saved_status() == fsa_saved_status::full) // we have something to merge { tmp_fsa = new (nothrow) filesystem_specific_attribute_list(); if(tmp_fsa == nullptr) throw Ememory("filtre.cpp::do_EFSA_transfer"); try { *tmp_fsa = *place_ino->get_fsa() + *add_ino->get_fsa(); // overwriting place_ino with add_ino's FSA place_ino->fsa_detach(); place_ino->fsa_attach(tmp_fsa); tmp_fsa = nullptr; } catch(...) { if(tmp_fsa != nullptr) { delete tmp_fsa; tmp_fsa = nullptr; } throw; } } else { if(add_ino->fsa_get_saved_status() == fsa_saved_status::full) { place_ino->fsa_set_saved_status(fsa_saved_status::full); tmp_fsa = new (nothrow) filesystem_specific_attribute_list(*add_ino->get_fsa()); if(tmp_fsa == nullptr) throw Ememory("filtre.cpp:do_EFSA_transfert"); try { place_ino->fsa_attach(tmp_fsa); tmp_fsa = nullptr; } catch(...) { if(tmp_fsa != nullptr) { delete tmp_fsa; tmp_fsa = nullptr; } throw; } } // else nothing to be done (in_place eventually has FSA and add_ino does nothing to add) } break; default: throw SRC_BUG; } } static void merge_ea(const ea_attributs & ref1, const ea_attributs & ref2, ea_attributs &res) { string ent_key, ent_val; string val; res = ref1; // assignment operator res.reset_read(); ref2.reset_read(); while(ref2.read(ent_key, ent_val)) if(!res.find(ent_key, val)) res.add(ent_key, ent_val); } static cat_entree *make_clone(const cat_nomme *ref, map & hard_link_base, const infinint & etiquette_offset) { cat_entree *dolly = nullptr; // will be the address of the cloned object string the_name; const cat_mirage *ref_mir = dynamic_cast(ref); if(ref == nullptr) throw SRC_BUG; the_name = ref->get_name(); if(ref_mir != nullptr) // this is hard linked inode { // check whether this is the first time we see this file (in list of file covered by the file masks) map ::iterator it = hard_link_base.find(ref_mir->get_etiquette() + etiquette_offset); if(it == hard_link_base.end()) // this inode has not been yet recorded in the resulting archive { cat_etoile *filante = nullptr; dolly = ref_mir->get_inode()->clone(); // we must clone the attached inode try { cat_inode *dollinode = dynamic_cast(dolly); if(dollinode == nullptr) throw Ememory("filtre:make_clone"); infinint shift_etiquette = ref_mir->get_etiquette() + etiquette_offset; filante = new (nothrow) cat_etoile(dollinode, shift_etiquette); if(filante == nullptr) throw Ememory("make_clone"); try { dolly = nullptr; // the inode is now managed by filante dolly = new (nothrow) cat_mirage(the_name, filante); if(dolly == nullptr) throw Ememory("make_clone"); try { hard_link_base[shift_etiquette] = filante; // we now record this file_etiquette in the map of already enrolled hard_link sets } catch(...) { filante = nullptr; // now managed by the cat_mirage pointed to by dolly throw; } } catch(...) { if(filante != nullptr) { delete filante; filante = nullptr; } throw; } } catch(...) { if(dolly != nullptr) { delete dolly; dolly = nullptr; } throw; } } else // already added to archive dolly = new (nothrow) cat_mirage(the_name, it->second); // we make a new cat_mirage pointing to the cat_etoile already involved in the catalogue under construction } else // not a hard_link file dolly = ref->clone(); // we just clone the entry if(dolly == nullptr) throw Ememory("make_clone"); return dolly; } static void clean_hard_link_base_from(const cat_mirage *mir, map & hard_link_base) { if(mir->get_etoile_ref_count().is_zero()) throw SRC_BUG; // count should be >= 1 if(mir->get_etoile_ref_count() == 1) { map::iterator it = hard_link_base.find(mir->get_etiquette()); const cat_inode *al_ptr_ino = mir->get_inode(); if(al_ptr_ino == nullptr) throw SRC_BUG; if(it == hard_link_base.end()) throw SRC_BUG; // the cat_etoile object pointed to by dolly_mir should be known by corres_copy hard_link_base.erase(it); } } static void normalize_link_base(map & hard_link_base) { infinint max_val = 0; map::iterator it, ut; infinint num_etoile = hard_link_base.size(); infinint search = 0; // first pass, looking highest etiquette number for(it = hard_link_base.begin(); it != hard_link_base.end(); ++it) { if(it->first > max_val) max_val = it->first; } // second pass, looking for holes and moving highest value to fill the gap in while(search < num_etoile) { it = hard_link_base.find(search); if(it == hard_link_base.end()) { // unused etiquette value, looking for the higest value do { if(max_val <= search) throw SRC_BUG; // num_etoile > 0, but we cannot find a cat_etoile to move into the hole ut = hard_link_base.find(max_val); --max_val; } while(ut == hard_link_base.end()); // we can now renumber cat_etoile tmp from max_val to search cat_etoile *tmp = ut->second; if(tmp == nullptr) throw SRC_BUG; tmp->change_etiquette(search); hard_link_base.erase(ut); hard_link_base[search] = tmp; } ++search; } } static const crit_action *make_overwriting_for_only_deleted() { const crit_action *ret = new (nothrow) testing(crit_invert(crit_in_place_is_inode()), crit_constant_action(data_preserve, EA_preserve), crit_constant_action(data_overwrite, EA_overwrite)); if(ret == nullptr) throw Ememory("make_overwriting_fir_only_deleted"); return ret; } static void save_delta_signature(const shared_ptr & dialog, const string & info_quoi, cat_file * e_file, const cat_file * ref_file, const pile_descriptor & pdesc, U_I signature_block_size, bool info_details, bool display_treated, const catalogue & cat) { if(e_file != nullptr && e_file->has_delta_signature_structure()) { shared_ptr sig; U_I block_size = signature_block_size; try { if(ref_file != nullptr && ref_file->has_delta_signature_structure()) { if(display_treated) dialog->message(string(gettext("Copying delta signature structure from the archive of reference: ")) + info_quoi); if(ref_file->has_delta_signature_available()) { ref_file->read_delta_signature(sig, block_size); // overwriting block size to fit the block // size used to create this signature if(!sig) throw SRC_BUG; } if(ref_file->has_patch_result_crc()) { const crc *tmp; ref_file->get_patch_result_crc(tmp); e_file->set_patch_result_crc(*tmp); } else throw SRC_BUG; } else // no ref_file inode, trying to calculate the delta signature from data { if(e_file->can_get_data() && e_file->get_saved_status() != saved_status::delta) // provided data is not a delta signature { null_file trou_noir(gf_write_only); generic_file *data = nullptr; if(display_treated) dialog->message(string(gettext("Calculating delta signature from filesystem: ")) + info_quoi); try // protecting data { infinint crc_size = tools_file_size_to_crc_size(e_file->get_size()); crc *patch_sig_crc = nullptr; try // protecting patch_sig_crc { sig.reset(new (nothrow) memory_file()); if(!sig) throw Ememory("filtre_sauvegarde"); data = e_file->get_data(cat_file::normal, sig, block_size, nullptr); if(data == nullptr) throw Ememory("filtre_sauvegarde"); data->copy_to(trou_noir, crc_size, patch_sig_crc); if(patch_sig_crc == nullptr) throw SRC_BUG; e_file->set_patch_base_crc(*patch_sig_crc); e_file->set_patch_result_crc(*patch_sig_crc); } catch(...) { if(patch_sig_crc != nullptr) delete patch_sig_crc; throw; } if(patch_sig_crc != nullptr) delete patch_sig_crc; } catch(...) { if(data != nullptr) delete data; throw; } if(data != nullptr) delete data; } else // no data or delta patch is available { if(e_file->get_saved_status() == saved_status::delta) throw SRC_BUG; e_file->read_delta_signature(sig, block_size); } } cat.pre_add_delta_sig(&pdesc); pdesc.compr->suspend_compression(); if(sig) e_file->dump_delta_signature(sig, block_size, *(pdesc.compr), pdesc.esc != nullptr); else e_file->dump_delta_signature(*(pdesc.compr), pdesc.esc != nullptr); } catch(...) { if(ref_file != nullptr) ref_file->drop_delta_signature_data(); if(e_file != nullptr) e_file->drop_delta_signature_data(); throw; } if(ref_file != nullptr) ref_file->drop_delta_signature_data(); if(e_file != nullptr) e_file->drop_delta_signature_data(); } } static bool furtive_check(bool furtive, const shared_ptr & dialog, bool verbose) { if(furtive && capability_FOWNER(*dialog, verbose) != capa_set && getuid() != 0) { if(verbose) dialog->printf(gettext("Furtive read mode requires either root permission and FOWNER capability, falling back to normal filesystem read")); furtive = false; } return furtive; } } // end of namespace dar-2.7.15/src/libdar/cat_nomme.cpp0000644000175000017500000000366314636066467014021 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_nomme.hpp" #include "tools.hpp" using namespace std; namespace libdar { cat_nomme::cat_nomme(const smart_pointer & pdesc, bool small, saved_status val) : cat_entree(pdesc, small, val) { pdesc->check(small); if(small) tools_read_string(*(pdesc->esc), xname); else tools_read_string(*(pdesc->stack), xname); } bool cat_nomme::operator == (const cat_entree & ref) const { const cat_nomme *ref_nomme = dynamic_cast(&ref); if(ref_nomme == nullptr) return false; else return xname == ref_nomme->xname; } void cat_nomme::inherited_dump(const pile_descriptor & pdesc, bool small) const { cat_entree::inherited_dump(pdesc, small); pdesc.check(small); if(small) tools_write_string(*pdesc.esc, xname); else tools_write_string(*pdesc.stack, xname); } } // end of namespace dar-2.7.15/src/libdar/slice_layout.hpp0000644000175000017500000000541414636066467014554 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file slice_layout.hpp /// \brief object describing the slicing of an archive /// \ingroup Private #ifndef SLICE_LAYOUT_HPP #define SLICE_LAYOUT_HPP #include #include #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ class slice_layout { public: slice_layout() { clear(); }; slice_layout(const slice_layout & ref) = default; slice_layout(slice_layout && ref) noexcept = default; slice_layout & operator = (const slice_layout & ref) = default; slice_layout & operator = (slice_layout && ref) noexcept = default; ~slice_layout() = default; // field still exposed (slice_layout was a struct before being a class) // we keep these fields as is for now infinint first_size; ///< size of the first slice infinint other_size; ///< maximum size of other slices infinint first_slice_header; ///< size of the slice header in the first slice infinint other_slice_header; ///< size of the slice header in the other slices bool older_sar_than_v8; ///< true if the archive format is older than version 8 void read(generic_file & f); void write(generic_file & f) const; void clear(); /// given a slice_layout and a archive offset, provides the corresponding slice and slice offset /// \param[in] offset input offset as if all slices were sticked toghether /// \param[out] slice_num slice number where to find the given offset /// \param[out] slice_offset offset in that slice where is the given offset void which_slice(const infinint & offset, infinint & slice_num, infinint & slice_offset) const; private: static const char OLDER_THAN_V8 = '7'; static const char V8 = '8'; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_ids.cpp0000644000175000017500000000672614636066467015105 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "filesystem_ids.hpp" #include "tools.hpp" #include "erreurs.hpp" using namespace std; namespace libdar { filesystem_ids::filesystem_ids(const path & root) { change_root_fs(root); } filesystem_ids::filesystem_ids(bool same_fs, const path & root) { change_root_fs(root); if(same_fs) included.insert(root_fs); else excluded.insert(root_fs); } void filesystem_ids::change_root_fs(const path & root) { root_fs = path2fs_id(root.display()); } void filesystem_ids::include_fs_at(const path & chem) { if(chem.is_relative()) throw Erange("filesystem_ids::set_root_fs","path to a filesystem must be an absolute path"); included.insert(path2fs_id(chem.display())); } void filesystem_ids::exclude_fs_at(const path & chem) { if(chem.is_relative()) throw Erange("filesystem_ids::set_root_fs","path to a filesystem must be an absolute path"); excluded.insert(path2fs_id(chem.display())); } bool filesystem_ids::is_covered(const infinint & fs_id) const { set::iterator it; if(fs_id == root_fs) return true; if(included.empty()) { if(excluded.empty()) return true; else { it = excluded.find(fs_id); return it == excluded.end(); } } else // included not empty { if(excluded.empty()) { it = included.find(fs_id); return it != included.end(); } else // both included and excluded are not empty { it = included.find(fs_id); if(it != included.end()) { it = excluded.find(fs_id); return it == excluded.end(); } else return false; } } } bool filesystem_ids::is_covered(const path & chem) const { if(chem.is_relative()) throw Erange("filesystem_ids::set_root_fs","path to a filesystem must be an absolute path"); return is_covered(path2fs_id(chem.display())); } infinint filesystem_ids::path2fs_id(const std::string & path) { struct stat buf; int val = 0; val = stat(path.c_str(), &buf); if(val < 0) { string errmsg = tools_strerror_r(errno); throw Erange("filesystem_ids", tools_printf(gettext("Cannot read filesystem information at %S: %S"), &path, &errmsg)); } return buf.st_dev; } } // end of namespace dar-2.7.15/src/libdar/list_entry.hpp0000644000175000017500000003460514636067146014253 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file list_entry.hpp /// \brief class of objects describing an entry in the archive, used by archive::get_children_in_table /// \ingroup API #ifndef LIST_ENTRY_HPP #define LIST_ENTRY_HPP #include #include #include "../my_config.h" #include "infinint.hpp" #include "deci.hpp" #include "compression.hpp" #include "integers.hpp" #include "datetime.hpp" #include "range.hpp" #include "cat_status.hpp" #include "ea.hpp" #include "fsa_family.hpp" namespace libdar { /// \addtogroup API /// @{ class crc; /// the list_entry class provides mean to get information about a particular entry in the archive /// /// it provides methods for libdar to fill up such object and methods for API user /// to read the information. Each information uses its own method, thus it will require /// several call to different method to get the full description of the object. /// This has the advantage to let the possiblity to add new fields in the future /// without breaking anything in API, and in consequences in user programs. class list_entry { public: list_entry() { clear(); }; list_entry(const list_entry & ref) = default; list_entry(list_entry && ref) = default; list_entry & operator = (const list_entry & ref) = default; list_entry & operator = (list_entry && ref) noexcept = default; ~list_entry() = default; /// method used to know whether the returned entry signals a End of Directory /// \note such entry does not contain any valid information, it signals the next /// entry will be taken from the parent directory, instead of the current directory. /// Such entry is necessary for archive::op_listing bool is_eod() const { return type == 'z'; }; // methods for API users // field that are not set are returned as empty string const std::string & get_name() const { return my_name; }; unsigned char get_type() const { return type; }; bool is_dir() const { return type == 'd'; }; bool is_file() const { return type == 'f'; }; bool is_symlink() const { return type == 'l'; }; bool is_char_device() const { return type == 'c'; }; bool is_block_device() const { return type == 'b'; }; bool is_unix_socket() const { return type == 's'; }; bool is_named_pipe() const { return type == 'p'; }; bool is_hard_linked() const { return hard_link; }; bool is_removed_entry() const { return type == 'x'; }; bool is_door_inode() const { return type == 'o'; }; bool is_empty_dir() const { return empty_dir; }; unsigned char get_removed_type() const; ///< valid only for removed_entries bool has_data_present_in_the_archive() const { return data_status == saved_status::saved || data_status == saved_status::delta; }; std::string get_data_flag() const; saved_status get_data_status() const { return data_status; }; bool has_EA() const { return ea_status != ea_saved_status::none && ea_status != ea_saved_status::removed; }; bool has_EA_saved_in_the_archive() const { return ea_status == ea_saved_status::full; }; std::string get_ea_flag() const; ea_saved_status get_ea_status() const { return ea_status; }; bool has_FSA() const { return fsa_status != fsa_saved_status::none; }; bool has_FSA_saved_in_the_archive() const { return fsa_status == fsa_saved_status::full; }; std::string get_fsa_flag() const; std::string get_uid(bool try_resolving_name = false) const; std::string get_gid(bool try_resolving_name = false) const; std::string get_perm() const; std::string get_last_access() const; std::string get_last_modif() const; std::string get_last_change() const; std::string get_removal_date() const; ///< for removed_entry only time_t get_last_access_s() const { return datetime2time_t(last_access); }; time_t get_last_modif_s() const; time_t get_last_change_s() const { return datetime2time_t(last_change); }; time_t get_removal_date_s() const; ///< for removed_entry only /// yet an alternative method to get last access time /// \param[in] tu time unit to be used to store fraction (libdar::datetime::tu_microsecond, libdar::datetime::tu_nanosecond,...) /// \param[out] second integer number of second /// \param[out] fraction remaining part of the time (expressed as tu unit) to be added to "second" to get the exact time void get_last_access(datetime::time_unit tu, time_t & second, time_t & fraction) const { last_access.get_value(second, fraction, tu); } /// yet an alternative method to get the last modification date (see get_last_access() for details) void get_last_modif(datetime::time_unit tu, time_t & second, time_t & fraction) const { last_modif.get_value(second, fraction, tu); } /// yet an alternative method to get the last change date (see get_last_access() for details) void get_last_change(datetime::time_unit tu, time_t & second, time_t & fraction) const { last_change.get_value(second, fraction, tu); } std::string get_file_size(bool size_in_bytes = true) const; std::string get_compression_ratio() const; std::string get_compression_ratio_flag() const; bool is_sparse() const { return sparse_file; }; std::string get_sparse_flag() const { return sparse_file ? "[X]" : "[ ]"; }; std::string get_compression_algo() const { return compression2string(compression_algo); }; bool is_dirty() const { return dirty; }; std::string get_link_target() const { return target; }; std::string get_major() const; std::string get_minor() const; /// provides slice information /// \note you must activate slice lookup from archive options for this /// field to me set by libdar, this operation has a additional cost in /// storage and computation const range & get_slices() const { return slices; }; bool has_delta_signature() const { return delta_sig; }; std::string get_delta_flag() const; /// offset in byte where to find first byte of data /// \note: return false if no data is present, else set the argument /// \note: offset is counted whatever is the number of slice as if there all slice were sticked toghether. But /// the first bytes of each slice do not count as they hold the slice header. This one is variable /// but can be known using the archive::get_first_slice_header_size() and archive::get_non_first_slice_header_size() /// methods from the current archive class. If encryption is used it is not possible to translate precisely from /// archive offset to slice offset, the encryption layer depending on the algorithm used may introduce an additional /// shift between clear data offset an corresponding ciphered data offset. /// \note if an U_64 cannot handle such large value, false is returned, you should use the infinint of std::string /// version of this method bool get_archive_offset_for_data(infinint & val) const { val = offset_for_data; return !val.is_zero(); }; bool get_archive_offset_for_data(U_64 & val) const; std::string get_archive_offset_for_data() const { return offset_for_data.is_zero() ? "" : deci(offset_for_data).human(); }; /// amount of byte used to store the file's data /// \note if an U_64 cannot handle such large value, false is returned, you should use the /// infinint of std::string version of this method bool get_storage_size_for_data(infinint & val) const { val = storage_size_for_data; return !val.is_zero(); }; bool get_storage_size_for_data(U_64 & val) const; std::string get_storage_size_for_data(bool size_in_bytes = true) const; /// offset in byte whert to find the first byte of Extended Attributes /// \note see note for get_archive_offset_for_data(infinint) /// \note if an U_64 cannot handle such large value, false is returned, you should use the infinint of /// std::string version of this method bool get_archive_offset_for_EA(infinint & val) const { val = offset_for_EA; return !val.is_zero(); }; bool get_archive_offset_for_EA(U_64 & val) const; std::string get_archive_offset_for_EA() const { return offset_for_EA.is_zero() ? "" : deci(offset_for_EA).human(); }; /// amount of byte used to store the file's EA bool get_storage_size_for_EA(infinint & val) const { val = storage_size_for_EA; return !val.is_zero(); }; bool get_storage_size_for_EA(U_64 & val) const; std::string get_storage_size_for_EA() const { return storage_size_for_EA.is_zero() ? "" : deci(storage_size_for_EA).human(); }; /// offset in byte where to find the first byte of Filesystem Specific Attributes /// \note see note for get_archive_offset_for_data(infinint) /// \note if an U_64 cannot handle such large value, false is returned, you should use the /// infinint of std::string version of this method bool get_archive_offset_for_FSA(infinint & val) const { val = offset_for_FSA; return !val.is_zero(); }; bool get_archive_offset_for_FSA(U_64 & val) const; std::string get_archive_offset_for_FSA() const { return offset_for_FSA.is_zero() ? "" : deci(offset_for_FSA).human(); }; /// amount of byte used to store the file's FSA bool get_storage_size_for_FSA(infinint & val) const { val = storage_size_for_FSA; return !val.is_zero(); }; bool get_storage_size_for_FSA(U_64 & val) const; std::string get_storage_size_for_FSA() const { return storage_size_for_FSA.is_zero() ? "" : deci(storage_size_for_FSA).human(); }; /// reset the reading of Extended Attributes names /// \note see get_ea_read_next() void get_ea_reset_read() const { it_ea = ea.begin(); }; /// read the next Extended Attribute name /// \param[out] key is set to the EA name /// \return true if some other attribute can be read, false if this one is the last /// \note to have list_entry objects filled with this information you need to /// activate it in archive options listing (archive::op_listing) of set fetch_ea to true /// in archive::get_children_of() and in archive_get_children_in_table() bool get_ea_read_next(std::string & key) const; std::string get_etiquette() const { return deci(etiquette).human(); }; ///< this is the hard-link ID, only valid for hard linked entries fsa_scope get_fsa_scope() const { return fsa_sc; }; std::string get_data_crc() const { return data_crc; }; std::string get_delta_patch_base_crc() const { return patch_base_crc; }; std::string get_delta_patch_result_crc() const { return patch_result_crc; }; // methods for libdar to setup the object void set_name(const std::string & val) { my_name = val; }; void set_type(unsigned char val) { type = val; }; void set_removed_type(unsigned char val); void set_hard_link(bool val) { hard_link = val; }; void set_uid(const infinint & val) { uid = val; }; void set_gid(const infinint & val) { gid = val; }; void set_perm(U_16 val) { perm = val; }; void set_last_access(const datetime & val) { last_access = val; }; void set_last_modif(const datetime & val) { last_modif = val; }; void set_removal_date(const datetime & val); void set_saved_status(saved_status val) { data_status = val; }; void set_ea_status(ea_saved_status val) { ea_status = val; }; void set_last_change(const datetime & val) { last_change = val; }; void set_fsa_status(fsa_saved_status val) { fsa_status = val; }; void set_file_size(const infinint & val) { file_size = val; }; void set_is_sparse_file(bool val) { sparse_file = val; }; void set_compression_algo(compression val) { compression_algo = val; }; void set_dirtiness(bool val) { dirty = val; }; void set_link_target(const std::string & val) { target = val; }; void set_major(int val) { major = val; }; void set_minor(int val) { minor = val; }; void set_slices(const range & sl) { slices = sl; }; void set_delta_sig(bool val) { delta_sig = val; }; void set_archive_offset_for_data(const infinint & val) { offset_for_data = val; }; void set_storage_size_for_data(const infinint & val) { storage_size_for_data = val; }; void set_archive_offset_for_EA(const infinint & val) { offset_for_EA = val; }; void set_storage_size_for_EA(const infinint & val) { storage_size_for_EA = val; }; void set_archive_offset_for_FSA(const infinint & val) { offset_for_FSA = val; }; void set_storage_size_for_FSA(const infinint & val) { storage_size_for_FSA = val; }; void set_ea(const ea_attributs & arg); void set_etiquette(const infinint & arg) { etiquette = arg; }; void set_fsa_scope(const fsa_scope & arg) { fsa_sc = arg; }; void set_data_crc(const crc & ptr); void set_delta_patch_base_crc(const crc & ptr); void set_delta_patch_result_crc(const crc & ptr); void set_empty_dir(bool val) { if(!is_dir()) throw SRC_BUG; empty_dir = val; }; void clear(); private: std::string my_name; bool hard_link; unsigned char type; infinint uid; infinint gid; U_16 perm; datetime last_access; datetime last_modif; ///< also used to store removal_date for removed entries saved_status data_status; ea_saved_status ea_status; datetime last_change; fsa_saved_status fsa_status; fsa_scope fsa_sc; infinint file_size; bool sparse_file; compression compression_algo; bool dirty; std::string target; ///< target[0] used to store signature of removed entry for removed entries int major; int minor; range slices; bool delta_sig; infinint offset_for_data; infinint storage_size_for_data; infinint offset_for_EA; infinint storage_size_for_EA; infinint offset_for_FSA; infinint storage_size_for_FSA; std::deque ea; mutable std::deque::const_iterator it_ea; infinint etiquette; ///< hard link identification std::string data_crc; std::string patch_base_crc; std::string patch_result_crc; bool empty_dir; static time_t datetime2time_t(const datetime & val); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/database.cpp0000644000175000017500000001616214636066467013621 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if STDC_HEADERS #include #endif #if HAVE_ERRNO_H #include #endif } // end extern "C" #include #include #include #include "nls_swap.hpp" #include "database.hpp" #include "i_database.hpp" using namespace libdar; using namespace std; namespace libdar { database::database(const shared_ptr & dialog) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_database(dialog)); if(!pimpl) throw Ememory("database::database"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } database::database(const shared_ptr & dialog, const string & base, const database_open_options & opt) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_database(dialog, base, opt)); if(!pimpl) throw Ememory("database::database"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } database::~database() = default; void database::dump(const std::string & filename, const database_dump_options & opt) const { NLS_SWAP_IN; try { pimpl->dump(filename, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::add_archive(const archive & arch, const string & chemin, const string & basename, const database_add_options & opt) { NLS_SWAP_IN; try { pimpl->add_archive(arch, chemin, basename, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::remove_archive(archive_num min, archive_num max, const database_remove_options & opt) { NLS_SWAP_IN; try { pimpl->remove_archive(min, max, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::set_permutation(archive_num src, archive_num dst) { NLS_SWAP_IN; try { pimpl->set_permutation(src, dst); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::change_name(archive_num num, const string & basename, const database_change_basename_options &opt) { NLS_SWAP_IN; try { pimpl->change_name(num, basename, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::set_path(archive_num num, const string & chemin, const database_change_path_options & opt) { NLS_SWAP_IN; try { pimpl->set_path(num, chemin, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::set_options(const vector & opt) { NLS_SWAP_IN; try { pimpl->set_options(opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::set_dar_path(const string & chemin) { NLS_SWAP_IN; try { pimpl->set_dar_path(chemin); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::set_compression(compression algozip) const { NLS_SWAP_IN; try { pimpl->set_compression(algozip); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::set_compression_level(U_I compr_level) const { NLS_SWAP_IN; try { pimpl->set_compression_level(compr_level); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } database_archives_list database::get_contents() const { database_archives_list ret; NLS_SWAP_IN; try { ret = pimpl->get_contents(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } vector database::get_options() const { vector ret; NLS_SWAP_IN; try { ret = pimpl->get_options(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } string database::get_dar_path() const { string ret; NLS_SWAP_IN; try { ret = pimpl->get_dar_path(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } compression database::get_compression() const { compression ret; NLS_SWAP_IN; try { ret = pimpl->get_compression(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } U_I database::get_compression_level() const { U_I ret; NLS_SWAP_IN; try { ret = pimpl->get_compression_level(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } string database::get_database_version() const { string ret; NLS_SWAP_IN; try { ret = pimpl->get_database_version(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } void database::get_files(database_listing_show_files_callback callback, void *context, archive_num num, const database_used_options & opt) const { NLS_SWAP_IN; try { pimpl->get_files(callback, context, num, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::get_version(database_listing_get_version_callback callback, void *context, path chemin) const { NLS_SWAP_IN; try { pimpl->get_version(callback, context, chemin); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::show_most_recent_stats(database_listing_statistics_callback callback, void *context) const { NLS_SWAP_IN; try { pimpl->show_most_recent_stats(callback, context); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void database::restore(const vector & filename, const database_restore_options & opt) { NLS_SWAP_IN; try { pimpl->restore(filename, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } bool database::check_order() const { bool ret; NLS_SWAP_IN; try { ret = pimpl->check_order(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } } // end of namespace dar-2.7.15/src/libdar/tronconneuse.hpp0000644000175000017500000002445214636066467014605 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file tronconneuse.hpp /// \brief defines a block structured file. /// \ingroup Private /// /// Mainly used for strong encryption. #ifndef TRONCONNEUSE_HPP #define TRONCONNEUSE_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "archive_version.hpp" #include "crypto_module.hpp" #include "proto_tronco.hpp" namespace libdar { /// \addtogroup Private /// @{ /// this is a partial implementation of the generic_file interface to cypher/decypher data block by block. /// tronconneuse is either read_only or write_only, read_write is not allowed. /// The openning mode is defined by encrypted_side's mode. /// In write_only no skip() is allowed, writing is sequential from the beginning of the file to the end /// (like writing to a pipe). /// In read_only all skip() functions are available. class tronconneuse : public proto_tronco { public: /// This is the constructor /// \param[in] block_size is the size of block encryption (the size of clear data encrypted together). /// \param[in] encrypted_side where encrypted data are read from or written to. /// \param[in] reading_ver version of the archive format /// \param[in] ptr pointer to a crypto_module object that will be passed to the tronconneuse object /// \note that encrypted_side is not owned and destroyed by tronconneuse, it must exist during all the life of the /// tronconneuse object, and is not destroyed by the tronconneuse's destructor tronconneuse(U_32 block_size, generic_file & encrypted_side, const archive_version & reading_ver, std::unique_ptr & ptr); /// copy constructor tronconneuse(const tronconneuse & ref) : proto_tronco(ref) { copy_from(ref); }; /// move constructor tronconneuse(tronconneuse && ref) noexcept: proto_tronco(std::move(ref)) { nullifyptr(); move_from(std::move(ref)); }; /// assignment operator tronconneuse & operator = (const tronconneuse & ref) { detruit(); proto_tronco::operator = (ref); copy_from(ref); return *this; }; /// move operator tronconneuse & operator = (tronconneuse && ref) noexcept { proto_tronco::operator = (std::move(ref)); move_from(std::move(ref)); return *this; }; /// destructor virtual ~tronconneuse() noexcept override { detruit(); }; // must not write pure virtual method from here, directly or not /// inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override; /// inherited from generic_file virtual bool skip(const infinint & pos) override; /// inherited from generic_file virtual bool skip_to_eof() override; /// inherited from generic_file virtual bool skip_relative(S_I x) override; /// inherited from generic_file virtual bool truncatable(const infinint & pos) const override { return false; }; /// inherited from generic_file virtual infinint get_position() const override { if(is_terminated()) throw SRC_BUG; return current_position; }; /// in write_only mode indicate that end of file is reached /// this call must be called in write mode to purge the /// internal cache before deleting the object (else some data may be lost) /// no further write call is allowed /// \note this call cannot be used from the destructor, because it relies on pure virtual methods virtual void write_end_of_file() override { if(is_terminated()) throw SRC_BUG; flush(); weof = true; }; /// this method to modify the initial shift. This overrides the constructor "no_initial_shift" of the constructor virtual void set_initial_shift(const infinint & x) override { initial_shift = x; }; /// let the caller give a callback function that given a generic_file with cyphered data, is able /// to return the offset of the first clear byte located *after* all the cyphered data, this /// callback function is used (if defined by the following method), when reaching End of File. virtual void set_callback_trailing_clear_data(trailing_clear_data_callback callback) override { trailing_clear_data = callback; }; /// returns the block size give to constructor virtual U_32 get_clear_block_size() const override { return clear_block_size; }; private: /// inherited from generic_file /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_read_ahead(const infinint & amount) override; /// this protected inherited method is now private for inherited classes of tronconneuse virtual U_I inherited_read(char *a, U_I size) override; /// inherited from generic_file /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_write(const char *a, U_I size) override; /// this prorected inherited method is now private for inherited classed of tronconneuse virtual void inherited_truncate(const infinint & pos) override { throw SRC_BUG; }; // no skippability in write mode, so no truncate possibility neither /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_sync_write() override { flush(); }; /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_flush_read() override { buf_byte_data = 0; }; /// this protected inherited method is now private for inherited classes of tronconneuse virtual void inherited_terminate() override { flush(); }; protected: const archive_version & get_reading_version() const { return reading_ver; }; private: infinint initial_shift; ///< the initial_shift first bytes of the underlying file are not encrypted // infinint buf_offset; ///< offset of the first byte in buf U_32 buf_byte_data; ///< number of byte of information in buf (buf_byte_data <= buf_size) U_32 buf_size; ///< size of allocated memory for clear data in buf char *buf; ///< decrypted data (or data to encrypt) // U_32 clear_block_size; ///< max amount of data that will be encrypted at once (must stay less than buf_size) infinint current_position; ///< position of the next character to read or write from the upper layer perspective, offset zero is the first encrypted byte, thus the first byte after initial_shift infinint block_num; ///< block number we next read or write generic_file *encrypted; ///< generic_file where is put / get the encrypted data // U_32 encrypted_buf_size; ///< allocated size of encrypted_buf U_32 encrypted_buf_data; ///< amount of byte of information in encrypted_buf char *encrypted_buf; ///< buffer of encrypted data (read or to write) // infinint extra_buf_offset; ///< offset of the first byte of extra_buf U_32 extra_buf_size; ///< allocated size of extra_buf U_32 extra_buf_data; ///< amount of byte of information in extra_buf char *extra_buf; ///< additional read encrypted that follow what is in encrypted_buf used to check for clear data after encrypted data // bool weof; ///< whether write_end_of_file() has been called bool reof; ///< whether we reached eof while reading archive_version reading_ver; ///< archive format we currently read std::unique_ptr crypto; ///< wraps the per block encryption/decryption routines trailing_clear_data_callback trailing_clear_data; ///< callback function that gives the amount of clear data found at the end of the given file void nullifyptr() noexcept; void detruit() noexcept; void copy_from(const tronconneuse & ref); void move_from(tronconneuse && ref) noexcept; U_32 fill_buf(); ///< returns the position (of the next read op) inside the buffer and fill the buffer with clear data void flush(); ///< flush any pending data (write mode only) to encrypted device void init_buf(); ///< initialize if necessary the various buffers that relies on inherited method values /// convert clear position to corresponding position in the encrypted data /// \param[in] pos is the position in the clear data /// \param[out] file_buf_start is the position of the beginning of the crypted block where can be found the data /// \param[out] clear_buf_start is the position of the beginning of the corresponding clear block /// \param[out] pos_in_buf is the position in the clear block of the 'pos' offset /// \param[out] block_num is the block number we have our requested position inside void position_clear2crypt(const infinint & pos, infinint & file_buf_start, infinint & clear_buf_start, infinint & pos_in_buf, infinint & block_num); /// gives the position of the next character /// of clear data that corresponds to the encrypted data which index is pos void position_crypt2clear(const infinint & pos, infinint & clear_pos); /// return true if a there is a byte of information at the given offset bool check_current_position() { return fill_buf() < buf_byte_data; }; /// remove clear data at the end of the encrypted_buf /// \param[in] crypt_offset is the offset of the first byte of encrypted_buf not /// considering initial_shift bytes before the begining of the encrypted data void remove_trailing_clear_data_from_encrypted_buf(const infinint & crypt_offset); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/sar.hpp0000644000175000017500000003221214636066467012641 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file sar.hpp /// \brief the sar and trivial_sar classes, they manage the slicing layer /// \ingroup Private #ifndef SAR_HPP #define SAR_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "generic_file.hpp" #include "header.hpp" #include "integers.hpp" #include "entrepot.hpp" #include "slice_layout.hpp" #include "contextual.hpp" #include "mem_ui.hpp" namespace libdar { // contextual is defined in generic_file module /// \addtogroup Private /// @{ /// Sar class stands for Segmentation And Reassembly class /// sar is used as a normal file but given some parameters at construction time /// the object will split the data in several files of given size (aka slices) /// sar uses a header to identify slices in a given set and tie slices of different sets /// At reading time sar transparently read data from the different slices. class sar : public generic_file, public contextual, protected mem_ui { public: /// this constructor reads data from a set of slices /// \param[in] dialog is for user interation (such a requesting a slice and pausing /// between slices) /// \param[in] base_name is the basename of all slices of the set (it will be added /// the "..extension" to form a filename /// \param[in] extension is the extension of slice's filenames /// \param[in] where defines where to store or where are stored slices /// \param[in] by_the_end if true dar will try to open the slice set starting from /// the last slice else it will try starting from the first /// \param[in] x_min_digits is the minimum number of digits the slices number is /// stored with in the filename /// \param[in] sequential_read to be set to true for sequential reading /// \param[in] lax if set to true will try workaround problems that would otherwise /// lead the operation to fail /// \param[in] execute is the command to execute before trying to open each slice /// for reading /// \note if by_the_end is set to true, the last slice must have extended slice /// header that contain informations about /// the first slice size (used starting archive format "08"), Else, the slice size /// is not possible to open as the offset /// of the data cannot be determined. If slice header is too old the sar class will /// fallback openning the first slice and /// directly get the first slice. sar(const std::shared_ptr & dialog, const std::string & base_name, const std::string & extension, const std::shared_ptr & where, bool by_the_end, const infinint & x_min_digits, bool sequential_read, bool lax = false, const std::string & execute = ""); /// this constructor creates a new set of slices /// \param[in,out] dialog is used for user interaction /// \param[in] open_mode read_write or write_only is accepted only /// \param[in] base_name is the slice set base name /// \param[in] extension is the slices extension /// \param[in] file_size is the size of slices (in byte) /// \param[in] first_file_size is the size of the first slice (in byte) or set it to zero if it has to be equal to other slice's size /// \param[in] x_warn_overwrite if set to true, a warning will be issued before overwriting a slice /// \param[in] x_allow_overwrite if set to false, no slice overwritting will be allowed /// \param[in] pause if set to zero no pause will be done between slice creation. If set to 1 a pause between each slice will be done. If set to N a pause each N slice will be done. Pauses must be acknoledged by user for the process to continue /// \param[in] where defines where to store the slices /// \param[in] internal_name is a tag common to all slice of the archive /// \param[in] data_name is a tag that has to be associated with the data. /// \param[in] force_permission if true slice permission will be forced to the value given in the next argument /// \param[in] permission value to use to set permission of slices /// \param[in] x_hash defines whether a hash file has to be generated for each slice, and wich hash algorithm to use /// \param[in] x_min_digits is the minimum number of digits the slices number is stored with in the filename /// \param[in] format_07_compatible when set to true, creates a slice header in the archive format of version 7 instead of the highest version known /// \param[in] execute is the command to execute after each slice creation (once it is completed) /// \note data_name should be equal to internal_name except when reslicing an archive as dar_xform does in which /// case internal_name is randomly, and data_name is kept from the source archive sar(const std::shared_ptr & dialog, gf_mode open_mode, const std::string & base_name, const std::string & extension, const infinint & file_size, const infinint & first_file_size, bool x_warn_overwrite, bool x_allow_overwrite, const infinint & pause, const std::shared_ptr & where, const label & internal_name, const label & data_name, bool force_permission, U_I permission, hash_algo x_hash, const infinint & x_min_digits, bool format_07_compatible, const std::string & execute = ""); /// the copy constructor sar(const sar & ref) = delete; /// move constructor sar(sar && ref) noexcept = delete; /// assignment operator sar & operator = (const sar & ref) = delete; /// move operator sar & operator = (sar && ref) noexcept = delete; /// destructor ~sar(); // inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override; virtual bool skip(const infinint &pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override; virtual infinint get_position() const override; // informational routines const slice_layout & get_slicing() const { return slicing; }; bool get_total_file_number(infinint &num) const { num = of_last_file_num; return of_last_file_known; }; bool get_last_file_size(infinint &num) const { num = of_last_file_size; return of_last_file_known; }; // disable execution of user command when destroying the current object void disable_natural_destruction() { natural_destruction = false; }; // enable back execution of user command when destroying the current object void enable_natural_destruction() { natural_destruction = true; }; // true if sar's header is from an old archive format (<= "07") virtual bool is_an_old_start_end_archive() const override { return slicing.older_sar_than_v8; }; // return the internal_name used to link slices toghether const label & get_internal_name_used() const { return of_internal_name; }; // return the data_name used to link slices toghether virtual const label & get_data_name() const override { return of_data_name; }; const std::shared_ptr & get_entrepot() const { return entr; }; /// get the first slice header const infinint & get_first_slice_header_size() const { return slicing.first_slice_header; }; /// get the non first slice header const infinint & get_non_first_slice_header_size() const { return slicing.other_slice_header; }; protected : virtual void inherited_read_ahead(const infinint & amount) override; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override {}; // nothing to do virtual void inherited_flush_read() override {}; // nothing to do virtual void inherited_terminate() override; private : std::shared_ptr entr; ///< where are stored slices std::string base; ///< archive base name std::string ext; ///< archive extension std::string hook; ///< command line to execute between slices slice_layout slicing; ///< slice layout infinint file_offset; ///< current reading/writing position in the current slice (relative to the whole slice file, including headers) hash_algo hash; ///< whether to build a hashing when creating slices, and if so, which algorithm to use infinint min_digits; ///< minimum number of digits the slices number is stored with in the filename bool natural_destruction; ///< whether to execute commands between slices on object destruction // these following variables are modified by open_file / open_file_init // else the are used only for reading infinint of_current; ///< number of the open slice infinint size_of_current; ///< size of the current slice (used in reading mode only) infinint of_max_seen; ///< highest slice number seen so far bool of_last_file_known; ///< whether the T terminal slice has been met infinint of_last_file_num; ///< number of the last slice (if met) infinint of_last_file_size; ///< size of the last slice (if met) label of_internal_name; ///< internal name shared in all slice header label of_data_name; ///< internal name linked to data (transparent to dar_xform and used by isolated catalogue as reference) bool force_perm; ///< true if any future slice has its permission to be set explicitely U_I perm; ///< if force_perm is true, value to use for slice permission fichier_global *of_fd; ///< file object currently openned char of_flag; ///< flags of the open file bool initial; ///< do not launch hook command-line during sar initialization // these are the option flags bool opt_warn_overwrite; ///< a warning must be issued before overwriting a slice bool opt_allow_overwrite; ///< is slice overwriting allowed // infinint pause; ///< do we pause between slices bool lax; ///< whether to try to go further reading problems infinint to_read_ahead; ///< amount of data to read ahead for next slices bool seq_read; ///< whether sequential read has been requested bool skip_forward(U_I x); ///< skip forward in sar global contents bool skip_backward(U_I x); ///< skip backward in sar global contents void close_file(bool terminal); ///< close current openned file, adding (in write mode only) a terminal mark (last slice) or not void open_readonly(const std::string & fic, ///< open file of name "fic" for read only const infinint &num, ///< "num" is the slice number bool bytheend ///< whether to position the read cursor at the beginning or the end of the file ); void open_writeonly(const std::string & fic, ///< open file of name "filename" for write only const infinint &num, ///< "num" is the slice number bool bytheend ///< whether to overwrite or to append data to be written to the file ); void open_file_init(); ///< initialize some of_* fields void open_file(infinint num, bool bytheend); ///< close current slice and open the slice 'num' void set_offset(infinint offset); ///< skip to current slice relative offset void open_last_file(bool bytheend); ///< open the last slice, ask the user, test, until last slice available bool is_current_eof_a_normal_end_of_slice() const; ///< return true if current reading position is at end of slice infinint bytes_still_to_read_in_slice() const; ///< returns the number of bytes expected before the end of slice header make_write_header(const infinint &num, char flag); // function to lauch the eventually existing command to execute after/before each slice void hook_execute(const infinint &num); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive_listing_callback.hpp0000644000175000017500000000312314636066467017041 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive_listing_callback.hpp /// \brief definition of the user defined callback function used for archive listing /// \ingroup API #include "list_entry.hpp" #ifndef ARCHIVE_LISTING_CALLBACK_HPP #define ARCHIVE_LISTING_CALLBACK_HPP #include "../my_config.h" namespace libdar { /// \addtogroup API /// @{ /// callback function type expected for archive::op_listing and archive::get_children_of() using archive_listing_callback = void (*)(const std::string & the_path, const list_entry & entry, void *context); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/entrepot_local.cpp0000644000175000017500000001042514636067146015056 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include extern "C" { #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "entrepot_local.hpp" #include "cygwin_adapt.hpp" #include "tools.hpp" #include "fichier_local.hpp" #include "user_interaction_blind.hpp" using namespace std; namespace libdar { entrepot_local::entrepot_local(const std::string & user, const std::string & group, bool x_furtive_mode) { furtive_mode = x_furtive_mode; contents = nullptr; set_user_ownership(user); set_group_ownership(group); set_root(tools_getcwd()); } entrepot_local & entrepot_local::operator = (const entrepot_local & ref) { entrepot *me = this; const entrepot *you = &ref; detruit(); *me = *you; // copying the entrepot part copy_from(ref); // copying the entrepot_local specific part return *this; } void entrepot_local::read_dir_reset() const { entrepot_local *me = const_cast(this); user_interaction_blind aveugle; if(me == nullptr) throw SRC_BUG; me->detruit(); me->contents = new (nothrow) etage(aveugle, get_location().display().c_str(), datetime(0), datetime(0), false, furtive_mode); if(contents == nullptr) throw Ememory("entrepot_local::read_dir_reset"); } bool entrepot_local::read_dir_next(string & filename) const { entrepot_local *me = const_cast(this); if(me == nullptr) throw SRC_BUG; if(contents == nullptr) return false; if(contents->fichier.empty()) { delete contents; me->contents = nullptr; return false; } filename = contents->fichier.front(); me->contents->fichier.pop_front(); return true; } fichier_global *entrepot_local::inherited_open(const shared_ptr & dialog, const std::string & filename, gf_mode mode, bool force_permission, U_I permission, bool fail_if_exists, bool erase) const { fichier_global *ret = nullptr; string fullname = (get_full_path().append(filename)).display(); U_I perm = force_permission ? permission : 0666; ret = new (nothrow) fichier_local(dialog, fullname, mode, perm, fail_if_exists, erase, false); if(ret == nullptr) throw Ememory("entrepot_local::inherited_open"); try { if(force_permission) ret->change_permission(permission); // this is necessary if the file already exists if(get_user_ownership() != "" || get_group_ownership() != "") { try { ret->change_ownership(get_user_ownership(), get_group_ownership()); } catch(Ebug & e) { throw; } catch(Egeneric & e) { e.prepend_message("Failed setting user and/or group ownership: "); throw Edata(e.get_message()); } } } catch(...) { delete ret; ret = nullptr; throw; } return ret; } void entrepot_local::inherited_unlink(const string & filename) const { string target = (get_full_path().append(filename)).display(); if(::unlink(target.c_str()) != 0) { string err = tools_strerror_r(errno); throw Erange("entrepot_local::inherited_unlink", tools_printf(gettext("Cannot remove file %s: %s"), target.c_str(), err.c_str())); } } } // end of namespace dar-2.7.15/src/libdar/range.hpp0000644000175000017500000001040714636066467013152 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file range.hpp /// \brief class than provide a way to manipulate and represent range of integer numbers (infinint) /// \ingroup API #ifndef RANGE_HPP #define RANGE_HPP #include #include #include #include "../my_config.h" #include "infinint.hpp" namespace libdar { /// \addtogroup API /// @{ /// stores a range of integers or a set of ranges class range { public: range() { clear(); }; range(const infinint & low, const infinint & high) { parts.push_back(segment(low, high)); }; range(const range & ref) = default; range(range && ref) noexcept = default; range & operator = (const range & ref) = default; range & operator = (range && ref) noexcept = default; ~range() = default; void operator += (const range & ref); range operator + (const range & ref) const { range ret = *this; ret += ref; return ret; }; std::string display() const; /// provides a way to read range contents segment by segment /// \note reset_read() is to be called once then read_next_segment() /// will return true for each new segment giving in argument its low and high value /// when no more segment are available it returns false, reset_read() can be call at /// any time to reset the reading operation void reset_read() const { read_cursor = parts.begin(); }; /// read the next available segment /// \param[out] low when read_next_segment() returns true, contains the low value of the next segment /// \param[out] high when read_next_segment() returns true, contains the high value of the next segment /// \return true and set the low and high value when a next segment is available in the range, returns /// false if all segment have been read low and high are not modified in that case. bool read_next_segment(infinint & low, infinint & high) const; void clear() { parts.clear(); }; private: class segment { public: segment(const infinint & x_low, const infinint & x_high) { low = x_low; high = x_high; }; const infinint & get_low() const { return low; }; const infinint & get_high() const { return high; }; bool overlaps_with(const segment & ref) const { return !(ref < *this) && !(ref > *this); }; void merge_with(const segment & ref); // only possible with a segment that overlaps with the current object // if two segment make < or > true they cannot be replaced by a single segment bool operator < (const segment & ref) const { return high + 1 < ref.low; }; bool operator > (const segment & ref) const { return ref < *this; }; bool operator == (const segment & ref) const { return ref.high == high && ref.low == low; }; bool operator != (const segment & ref) const { return ! (*this == ref); }; // if two segment make <= or >= true they can be replaced by a single (larger) segment bool operator <= (const segment & ref) const { return ref.low < low && low <= ref.high + 1 && ref.high < high; }; bool operator >= (const segment &ref) const { return ref <= *this; }; bool contains(const segment & ref) const { return low <= ref.low && ref.high <= high; }; std::string display() const; private: infinint low, high; }; std::list parts; mutable std::list::const_iterator read_cursor; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cygwin_adapt.h0000644000175000017500000000333514636066467014171 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cygwin_adapt.h /// \brief thin C adaptation layer to Cygwin specifities /// \ingroup Private #ifndef CYGWIN_ADAPT_H #define CYGWIN_ADAPT_H #include "../my_config.h" /// \addtogroup Private /// @{ #if HAVE_FCNTL_H #include #endif // if fcntl.h does not define O_TEXT nor O_BINARY (which is a Cygwin // speciality), we define them as neutral ORed values : zero #ifndef O_TEXT // zero is neutral in ORed expression where it is expected to be used #define O_TEXT 0 #endif #ifndef O_BINARY // zero is neutral in ORed expression where it is expected to be used #define O_BINARY 0 #else #define CYGWIN_BUILD 1 // if O_BINARY is defined we are compiling on or for a cygwin plateform #endif /// @} #endif dar-2.7.15/src/libdar/cache_global.cpp0000644000175000017500000000325214636067146014427 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "cache_global.hpp" using namespace std; namespace libdar { cache_global::cache_global(const shared_ptr & dialog, fichier_global *x_ptr, bool shift_mode, U_I size) : fichier_global(dialog, x_ptr == nullptr ? throw SRC_BUG : x_ptr->get_mode()) { ptr = x_ptr; buffer = new (nothrow) cache(*ptr, shift_mode, size); if(buffer == nullptr) throw Ememory("cache_global::cache_global"); } void cache_global::detruit() { if(buffer != nullptr) { delete buffer; buffer = nullptr; } if(ptr != nullptr) { delete ptr; ptr = nullptr; } } } // end of namespace dar-2.7.15/src/libdar/bzip2_module.hpp0000644000175000017500000000456714636066467014463 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file bzip2_module.hpp /// \brief per block encryption using bzip2 algorithm/library /// \ingroup Private /// #ifndef BZIP2_MODULE_HPP #define BZIP2_MODULE_HPP extern "C" { } #include "../my_config.h" #include "compress_module.hpp" #include "infinint.hpp" namespace libdar { /// \addtogroup Private /// @{ class bzip2_module: public compress_module { public: bzip2_module(U_I compression_level = 9); bzip2_module(const bzip2_module & ref) = default; bzip2_module(bzip2_module && ref) noexcept = default; bzip2_module & operator = (const bzip2_module & ref) = default; bzip2_module & operator = (bzip2_module && ref) noexcept = default; virtual ~bzip2_module() noexcept = default; // inherited from compress_module interface virtual compression get_algo() const override { return compression::bzip2; }; virtual U_I get_max_compressing_size() const override; virtual U_I get_min_size_to_compress(U_I clear_size) const override; virtual U_I compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const override; virtual U_I uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const override; virtual std::unique_ptr clone() const override; private: U_I level; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_blockdev.hpp0000644000175000017500000000557214636066467014505 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_blockdev.hpp /// \brief class used to record block special devices in a catalogue /// \ingroup Private #ifndef CAT_BLOCKDEV_HPP #define CAT_BLOCKDEV_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_device.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the block device class class cat_blockdev : public cat_device { public: cat_blockdev(const infinint & uid, const infinint & gid, U_16 perm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & name, U_16 major, U_16 minor, const infinint & fs_device) : cat_device(uid, gid, perm, last_access, last_modif, last_change, name, major, minor, fs_device) {}; cat_blockdev(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small) : cat_device(dialog, pdesc, reading_ver, saved, small) {}; cat_blockdev(const cat_blockdev & ref) = default; cat_blockdev(cat_blockdev && ref) noexcept = default; cat_blockdev & operator = (const cat_blockdev & ref) = default; cat_blockdev & operator = (cat_blockdev && ref) = default; ~cat_blockdev() = default; virtual bool operator == (const cat_entree & ref) const override; // using dump from cat_device class // using method is_more_recent_than() from cat_device class // using method has_changed_since() from cat_device class virtual unsigned char signature() const override { return 'b'; }; virtual std::string get_description() const override { return "block device"; }; virtual cat_entree *clone() const override { return new (std::nothrow) cat_blockdev(*this); }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive_options5.hpp0000644000175000017500000001454614636067146015342 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive_options5.hpp /// \brief API v5 compatible archive_options_* classes /// \ingroup API5 #ifndef ARCHIVE_OPTIONS5_HPP #define ARCHIVE_OPTIONS5_HPP #include "archive_options.hpp" #include "archive_options_listing_shell.hpp" namespace libdar5 { // /////////////////////////////////////////////////////// // //////////// OPTIONS FOR OPENNING AN ARCHIVE ////////// // /////////////////////////////////////////////////////// /// \addtogroup API5 /// @{ using libdar::entrepot; class archive_options_read: public libdar::archive_options_read { public: void set_entrepot(const entrepot & entr) { std::shared_ptr tmp(entr.clone()); if(!tmp) throw Ememory("libdar5::archive_options_create::set_entrepot"); libdar::archive_options_read::set_entrepot(tmp); } void set_ref_entrepot(const entrepot & entr) { std::shared_ptr tmp(entr.clone()); if(!tmp) throw Ememory("libdar5::archive_options_create::set_ref_entrepot"); libdar::archive_options_read::set_ref_entrepot(tmp); } }; // /////////////////////////////////////////////////////// // /////// OPTIONS FOR CREATING AN ARCHIVE /////////////// // /////////////////////////////////////////////////////// class archive_options_create: public libdar::archive_options_create { public: using libdar::archive_options_create::archive_options_create; /// backward API v5 compatible method /// \note in API v5, set_reference() was given a pointer /// which was kept under the responsibility of the caller /// the API was not deleting it. /// In API v6 it was decided to remove ambiguity by /// using a std::shared_ptr, which bioth shows /// that archive object will not be deleted by libdar and /// also clearly managed the release of the object when /// no more needed/used. /// Here to adapt APIv5, we have to build a std::shared_ptr /// but as the object must survive the last shared_ptr /// pointing to (APIv5 caller will delete it), the shared_ptr /// is setup with a custome deleter which is a lambda expression /// that does nothing [](libdar::archive *ptr) {} void set_reference(libdar::archive *ref_arch) { libdar::archive_options_create::set_reference( std::shared_ptr(ref_arch, [](libdar::archive* ptr) {} ) // the custom deleter must not delete the object pointed to by // by ref, this is why it does nothing ); }; void set_entrepot(const entrepot & entr) { std::shared_ptr tmp(entr.clone()); if(!tmp) throw Ememory("libdar5::archive_options_create::set_entrepot"); libdar::archive_options_create::set_entrepot(tmp); } }; // /////////////////////////////////////////////////////// // ////////// OPTIONS FOR ISOLATING A CATALOGUE ////////// // /////////////////////////////////////////////////////// class archive_options_isolate: public libdar::archive_options_isolate { void set_entrepot(const entrepot & entr) { std::shared_ptr tmp(entr.clone()); if(!tmp) throw Ememory("libdar5::archive_options_create::set_entrepot"); libdar::archive_options_isolate::set_entrepot(tmp); } }; // /////////////////////////////////////////////////////// // //////// OPTONS FOR MERGING ARCHIVES ////////////////// // /////////////////////////////////////////////////////// class archive_options_merge: public libdar::archive_options_merge { public: using libdar::archive_options_merge::archive_options_merge; void set_auxilliary_ref(libdar::archive *ref) { libdar::archive_options_merge::set_auxiliary_ref( std::shared_ptr(ref, [](libdar::archive* ptr) {}) // the custom deleter must not delete the object pointed to by // by ref, this is why it does nothing ); }; void set_entrepot(const entrepot & entr) { std::shared_ptr tmp(entr.clone()); if(!tmp) throw Ememory("libdar5::archive_options_create::set_entrepot"); libdar::archive_options_merge::set_entrepot(tmp); } }; // /////////////////////////////////////////////////////// // //////// OPTONS FOR EXTRACTING FILES FROM ARCHIVE ///// // /////////////////////////////////////////////////////// using libdar::archive_options_extract; // /////////////////////////////////////////////////////// // //////// OPTIONS FOR LISTING AN ARCHIVE /////////////// // /////////////////////////////////////////////////////// class archive_options_listing: public libdar::archive_options_listing_shell { public: using libdar::archive_options_listing_shell::archive_options_listing_shell; }; // /////////////////////////////////////////////////////// // //////// OPTONS FOR DIFFING AN ARCHIVE //////////////// // /////////////////////////////////////////////////////// using libdar::archive_options_diff; // /////////////////////////////////////////////////////// // //////// OPTONS FOR TESTING AN ARCHIVE //////////////// // /////////////////////////////////////////////////////// using libdar::archive_options_test; // /////////////////////////////////////////////////////// // /////// OPTIONS FOR REPAIRING AN ARCHIVE ////////////// // /////////////////////////////////////////////////////// using libdar::archive_options_repair; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_hard_link_write.cpp0000644000175000017500000003627414636066467017474 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif #if HAVE_TIME_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_SYS_UN_H #include #endif #if HAVE_UNISTD_H #include #endif #if STDC_HEADERS #include #endif #ifdef LIBDAR_NODUMP_FEATURE #if HAVE_SYS_IOCTL_H #include #endif #if LIBDAR_NODUMP_FEATURE == NODUMP_LINUX #include #else #if LIBDAR_NODUMP_FEATURE == NODUMP_EXT2FS #include #else #error "unknown location of ext2_fs.h include file" #endif #endif #endif #if MAJOR_IN_MKDEV #include #if !defined(makedev) && defined(mkdev) #define makedev(a,b) mkdev((a),(b)) #endif #else #if MAJOR_IN_SYSMACROS #include #endif #endif } // end extern "C" #include #include #include "filesystem_hard_link_write.hpp" #include "tools.hpp" #include "erreurs.hpp" #include "user_interaction.hpp" #include "cat_all_entrees.hpp" #include "ea_filesystem.hpp" #include "cygwin_adapt.hpp" #include "fichier_local.hpp" #include "generic_rsync.hpp" #include "null_file.hpp" #include "filesystem_tools.hpp" #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 104 #endif using namespace std; namespace libdar { bool filesystem_hard_link_write::raw_set_ea(const cat_nomme *e, const ea_attributs & list_ea, const string & spot, const mask & ea_mask) { const cat_mirage *e_mir = dynamic_cast(e); bool ret = false; try { if(e == nullptr) throw SRC_BUG; // checking that we have not already restored the EA of this // inode through another hard link if(e_mir != nullptr) { map::iterator it; it = corres_write.find(e_mir->get_etiquette()); if(it == corres_write.end()) { // inode never restored; (no data saved just EA) // we must record it corres_ino_ea tmp; tmp.chemin = spot; tmp.ea_restored = true; corres_write[e_mir->get_etiquette()] = tmp; } else if(it->second.ea_restored) return false; // inode already restored else it->second.ea_restored = true; } // restoring Extended Attributes // (void)ea_filesystem_write_ea(spot, list_ea, ea_mask); ret = true; } catch(Euser_abort & e) { ret = false; } return ret; } bool filesystem_hard_link_write::raw_clear_ea_set(const cat_nomme *e, const string & spot) { const cat_mirage *e_mir = dynamic_cast(e); bool ret = false; try { if(e == nullptr) throw SRC_BUG; // checking that we have not already restored the EA of this // inode through another hard link if(e_mir != nullptr) { map::iterator it; it = corres_write.find(e_mir->get_etiquette()); if(it == corres_write.end()) { // inode never restored; (no data saved just EA) // we must record it corres_ino_ea tmp; tmp.chemin = spot; tmp.ea_restored = false; // clearing the EA does not mean we have restored them, setting here "false" let raw_set_ea() being call afterward corres_write[e_mir->get_etiquette()] = tmp; } else // entry found if(it->second.ea_restored) return false; // inode already restored } // Clearing all EA // ea_filesystem_clear_ea(spot, bool_mask(true)); ret = true; } catch(Euser_abort & e) { ret = false; } return ret; } void filesystem_hard_link_write::write_hard_linked_target_if_not_set(const cat_mirage *ref, const string & chemin) { if(!known_etiquette(ref->get_etiquette())) { corres_ino_ea tmp; tmp.chemin = chemin; tmp.ea_restored = false; // if EA have to be restored next corres_write[ref->get_etiquette()] = tmp; } } bool filesystem_hard_link_write::known_etiquette(const infinint & eti) { return corres_write.find(eti) != corres_write.end(); } void filesystem_hard_link_write::make_file(const cat_nomme * ref, const path & ou) { const cat_directory *ref_dir = dynamic_cast(ref); const cat_file *ref_fil = dynamic_cast(ref); const cat_lien *ref_lie = dynamic_cast(ref); const cat_blockdev *ref_blo = dynamic_cast(ref); const cat_chardev *ref_cha = dynamic_cast(ref); const cat_tube *ref_tub = dynamic_cast(ref); const cat_prise *ref_pri = dynamic_cast(ref); const cat_mirage *ref_mir = dynamic_cast (ref); const cat_inode *ref_ino = dynamic_cast (ref); if(ref_ino == nullptr && ref_mir == nullptr) throw SRC_BUG; // neither an cat_inode nor a hard link const string display = (ou.append(ref->get_name())).display(); const char *name = display.c_str(); S_I ret = -1; // will carry the system call returned value used to create the requested file do { try { if(ref_mir != nullptr) // we potentially have to make a hard link { bool create_file = false; map::iterator it = corres_write.find(ref_mir->get_etiquette()); if(it == corres_write.end()) // first time, we have to create the inode create_file = true; else // the inode already exists, making hard link if possible { const char *old = it->second.chemin.c_str(); ret = link(old, name); if(ret < 0) { string tmp; switch(errno) { case EXDEV: // crossing filesystem case EPERM: // filesystem does not support hard link creation // can't make hard link, trying to duplicate the inode tmp = tools_strerror_r(errno); get_ui().message(tools_printf(gettext("Error creating hard link %s : %s\n Trying to duplicate the inode"), name, tmp.c_str())); create_file = true; clear_corres_if_pointing_to(ref_mir->get_etiquette(), old); // always succeeds as the etiquette points to "old" // need to remove this entry to be able // to restore EA for other copies break; case ENOENT: // path to the hard link to create does not exit if(ref_mir->get_inode()->get_saved_status() == saved_status::saved) { create_file = true; clear_corres_if_pointing_to(ref_mir->get_etiquette(), old); // always succeeds as the etiquette points to "old" // need to remove this entry to be able // to restore EA for other copies get_ui().message(tools_printf(gettext("Error creating hard link : %s , the inode to link with [ %s ] has disappeared, re-creating it"), name, old)); } else { create_file = false; // nothing to do; get_ui().message(tools_printf(gettext("Error creating hard link : %s , the inode to link with [ %s ] is not present, cannot restore this hard link"), name, old)); } break; default : // nothing to do (ret < 0 and create_file == false) break; } } else create_file = false; } if(create_file) { ref_ino = ref_mir->get_inode(); ref_fil = dynamic_cast(ref_mir->get_inode()); ref_lie = dynamic_cast(ref_mir->get_inode()); ref_blo = dynamic_cast(ref_mir->get_inode()); ref_cha = dynamic_cast(ref_mir->get_inode()); ref_tub = dynamic_cast(ref_mir->get_inode()); ref_pri = dynamic_cast(ref_mir->get_inode()); ref_mir->get_inode()->change_name(ref_mir->get_name()); // we temporarily change the name of the attached inode (it is not used usually), by the name of the cat_mirage object } else // hard link made ret = 0; // not necessary, but avoids a warning from compilator ("ret" might be used uninitialized) } // build plain inode object (or initial inode for hard link --- if create_file was true above) if(ref_dir != nullptr) { ret = mkdir(name, 0700); // as the directory has been created we are its owner and we will need only // to create files under it so we need all rights for user, by security for now, no right are // allowed for group and others, but this will be set properly at the end, when all files will // be restored in that directory } else if(ref_fil != nullptr) { generic_file *ou; infinint seek; fichier_local dest = fichier_local(get_pointer(), display, gf_write_only, 0700, false, true, false); // the implicit destruction of dest (exiting the block) // will close the 'ret' file descriptor (see ~fichier_local()) ou = ref_fil->get_data(cat_file::normal, nullptr, 0, nullptr); try { const crc *crc_ori = nullptr; crc *crc_dyn = nullptr; infinint crc_size; try { if(!ref_fil->get_crc_size(crc_size)) crc_size = tools_file_size_to_crc_size(ref_fil->get_size()); ou->skip(0); ou->read_ahead(ref_fil->get_storage_size()); ou->copy_to(dest, crc_size, crc_dyn); if(crc_dyn == nullptr) throw SRC_BUG; if(ref_fil->get_crc(crc_ori)) { if(crc_ori == nullptr) throw SRC_BUG; if(typeid(*crc_dyn) != typeid(*crc_ori)) throw SRC_BUG; if(*crc_dyn != *crc_ori) throw Erange("filesystem_hard_link_write::make_file", gettext("Bad CRC, data corruption occurred")); // else nothing to do, nor to signal } // else this is a very old archive } catch(...) { if(crc_dyn != nullptr) delete crc_dyn; throw; } if(crc_dyn != nullptr) delete crc_dyn; try { // nop we do not sync before, so maybe some pages // will be kept in cache for Linux, maybe not for // other systems that support fadvise(2) dest.fadvise(fichier_global::advise_dontneed); } catch(Erange & e) { // silently ignoring any fadvise error // this is not crucial anyway } } catch(...) { if(ou != nullptr) delete ou; throw; } delete ou; ret = 0; // to report a successful operation at the end of the if/else if chain } else if(ref_lie != nullptr) ret = symlink(ref_lie->get_target().c_str(), name); else if(ref_blo != nullptr) ret = mknod(name, S_IFBLK | 0700, makedev(ref_blo->get_major(), ref_blo->get_minor())); else if(ref_cha != nullptr) ret = mknod(name, S_IFCHR | 0700, makedev(ref_cha->get_major(), ref_cha->get_minor())); else if(ref_tub != nullptr) ret = mknod(name, S_IFIFO | 0700, 0); else if(ref_pri != nullptr) { ret = socket(PF_UNIX, SOCK_STREAM, 0); if(ret >= 0) { S_I sd = ret; struct sockaddr_un addr; addr.sun_family = AF_UNIX; try { strncpy(addr.sun_path, name, UNIX_PATH_MAX - 1); addr.sun_path[UNIX_PATH_MAX - 1] = '\0'; if(strlen(addr.sun_path) < strlen(name)) get_ui().pause(tools_printf(gettext("error restoring Unix socket %s, path too long to be stored properly, socket will be created as %s instead, do you confirm?"), name, addr.sun_path)); if(::bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) throw Erange("filesystem_hard_link_write::make_file (socket bind)", string(gettext("Error creating Unix socket file: ")) + name + " : " + tools_strerror_r(errno)); } catch(...) { shutdown(sd, 2); close(sd); throw; } shutdown(sd, 2); close(sd); } } else if(ref_mir == nullptr) throw SRC_BUG; // unknown inode type // else nothing do do cat_mirage have been handled far above if(ret < 0) { if(errno != ENOSPC) throw Erange("filesystem_hard_link_write::make_file", string(gettext("Could not create inode: ")) + name + " : " + tools_strerror_r(errno)); else get_ui().pause(string(gettext("Cannot create inode: ")) + tools_strerror_r(errno) + gettext(" Ready to continue ?")); } else // inode successfully created if(ref_mir != nullptr) { map::iterator it = corres_write.find(ref_mir->get_etiquette()); if(it == corres_write.end()) // we just created the first hard linked to that inode, so we must record its intial link to build subsequent ones { corres_ino_ea tmp; tmp.chemin = string(name); tmp.ea_restored = false; corres_write[ref_mir->get_etiquette()] = tmp; } } } catch(Ethread_cancel & e) { if(ret >= 0) // we need to remove the filesystem entry we were creating // we don't let something uncompletely restored in the filesystem // in any case (immediate cancel or not) (void)unlink(name); // ignoring exit status throw; } } while(ret < 0 && errno == ENOSPC); } void filesystem_hard_link_write::clear_corres_if_pointing_to(const infinint & ligne, const string & path) { map::iterator it = corres_write.find(ligne); if(it != corres_write.end()) { if(it->second.chemin == path) corres_write.erase(it); } } } // end of namespace dar-2.7.15/src/libdar/elastic.cpp0000644000175000017500000002356614636066467013507 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STDLIB_H #include #endif #if HAVE_TIME_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_GCRYPT_H #ifndef GCRYPT_NO_DEPRECATED #define GCRYPT_NO_DEPRECATED #endif #include #endif } #include "elastic.hpp" #include "infinint.hpp" #include "header_version.hpp" #include "tools.hpp" #define SINGLE_MARK 'X' //// elastic buffer structure //// // // .......LOOOOH..... // // where '.' are random bytes and // 'L' and 'H' are LOW marks and HIGH mark respectively // OOOOO is a byte field giving the size in byte of the total // elastic buffer (big endian), written in base 254 (values corresponding to LOW and HIGH marks cannot be used here) // // before archive format "07" the size was written in base 256 which sometimes // lead this field to containe a low or high mark. At reading time it was not // possible to determin the effective size of the elastic buffer due to confusion // with this mark value inside the size field. // // some trivial elastic buffer is 'X' for a buffer of size 1 (where 'X' // is the "single mark") // and 'LH' for an elastic buffer of size 2 //////////////////////////////////// using namespace std; namespace libdar { elastic::elastic(U_32 size) { if(size == 0) throw Erange("elastic::elastic", gettext("Zero is not a valid size for an elastic buffer")); if(size > max_length()) throw Erange("elastic::elastic", gettext("Size too large for an elastic buffer")); taille = size; } elastic::elastic(const unsigned char *buffer, U_32 size, elastic_direction dir, const archive_version & reading_ver) { U_32 pos = (dir == elastic_forward ? 0 : size - 1); const U_32 first_pos = pos; S_I step = (dir == elastic_forward ? +1 : -1); unsigned char first_mark = (dir == elastic_forward ? get_low_mark(reading_ver) : get_high_mark(reading_ver)); unsigned char last_mark = (dir == elastic_forward ? get_high_mark(reading_ver) : get_low_mark(reading_ver)); static const U_32 forbidden_size = ~((U_32)(0)); // see note below if(size >= forbidden_size) throw Erange("elastic::elastic", gettext("Buffer size too large to setup an elastic buffer")); while(pos < size && buffer[pos] != SINGLE_MARK && buffer[pos] != first_mark) pos += step; // note: when going backward, the 'pos < size' condition stays pertinent // because 'pos' is an unsigned int and when decrementing by one a value of zero, it // loops to the higest integer supported by this unsigned int type, which is // greater (or equal) to 'size'. The out of bound condition is thus detected by the // 'pos < size' statement in both forward and backward directions // // in forward direction however, having 'size' equal to the max integer value supported // will not lead to detect this overhead, so we forbids size to be that max value. // In practice a such large elastic buffer (2^32 bytes wide) is never used in libdar // by default. // // Thanks to Sviat89@github for triggering this. if(pos >= size) throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); if(buffer[pos] == SINGLE_MARK) if(first_pos == pos) taille = 1; else throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); else // elastic buffer size is greater than one { U_32 power_base = 1; U_I base = base_from_version(reading_ver); const U_32 int_width = sizeof(U_32); U_32 byte_counter = 0; // now reading total size pos += step; taille = 0; while(pos < size && buffer[pos] != last_mark) { if(dir != elastic_forward) { taille *= base; taille += buffer[pos]; } else { taille += power_base * buffer[pos]; power_base *= base; } pos += step; if(++byte_counter > int_width) throw Erange("elastic::elastic", gettext("too large elastic buffer or elastic buffer incoherent structure")); } if(pos >= size) throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); if(taille == 0 && byte_counter == 0) taille = 2; // this is the trivial buffer of size 2 else if(taille < 3) throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); } } elastic::elastic(generic_file &f, elastic_direction dir, const archive_version & reading_ver) { U_32 count = 0; S_I (generic_file::*lecture)(char &a) = (dir == elastic_forward ? &generic_file::read_forward : &generic_file::read_back); unsigned char first_mark = (dir == elastic_forward ? get_low_mark(reading_ver) : get_high_mark(reading_ver)); unsigned char last_mark = (dir == elastic_forward ? get_high_mark(reading_ver) : get_low_mark(reading_ver)); unsigned char a = '\0'; // initialized just to avoid cppcheck complaining while((f.*lecture)((char &)a) && a != SINGLE_MARK && a != first_mark) ++count; if(a != SINGLE_MARK && a != first_mark) throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); else ++count; if(a == SINGLE_MARK) if(count == 1) taille = 1; else throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); else // elastic buffer size is greater than one { U_32 power_base = 1; U_I base = base_from_version(reading_ver); const U_32 int_width = sizeof(U_32); U_32 byte_counter = 0; // now reading total size taille = 0; while((f.*lecture)((char &)a) && a != last_mark) { if(dir != elastic_forward) { taille *= base; taille += a; } else { taille += power_base * a; power_base *= base; } count++; if(++byte_counter > int_width) throw Erange("elastic::elastic", gettext("too large elastic buffer or elastic buffer incoherent structure")); } if(a != last_mark) throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); else count++; if(taille == 0 && byte_counter == 0) taille = 2; // this is the trivial buffer of size 2 else if(taille < 3) throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); // now skipping to the "end" of the elastic buffer if(count < taille) if(dir == elastic_forward) f.skip_relative(taille - count); else f.skip_relative(count - taille); else if(count > taille) throw Erange("elastic::elastic", gettext("elastic buffer incoherent structure")); } } U_32 elastic::dump(unsigned char *buffer, U_32 size) const { if(size < taille) throw Erange("elastic::dump", gettext("not enough space provided to dump the elastic buffer")); if(taille > 2) { U_32 pos; U_32 cur; U_32 len; static const unsigned char base = 254; deque digits = tools_number_base_decomposition_in_big_endian(taille, (unsigned char)(base)); U_I seed = ::time(nullptr)+getpid(); #if CRYPTO_AVAILABLE U_I stronger; gcry_create_nonce((unsigned char*) &stronger, sizeof(stronger)); seed ^= stronger; #endif // let's randomize a little more srand(seed); //// // choosing the location of the first marks and following informations len = digits.size(); // len is the number of byte taken by the size information to encode if(len + 2 > taille) // +2 marks throw SRC_BUG; // a value takes more bytes to be stored than its own value !!! // (is only true for zero, which is an invalid "taille"). if(len + 2 < taille) cur = rand() % (taille - (len + 2)); // cur is the position of the first mark else // len + 2 == taille cur = 0; // filling non information bytes with random values for(pos = 0; pos < cur; ++pos) randomize(buffer+pos); buffer[cur++] = get_low_mark(); pos = 0; while(pos < len) buffer[cur++] = digits[pos++]; buffer[cur++] = get_high_mark(); // filling the rest of the non information byte with random values for(pos = cur; pos < taille; ++pos) randomize(buffer+pos); } else if(taille == 1) buffer[0] = SINGLE_MARK; else if(taille == 2) { buffer[0] = get_low_mark(); buffer[1] = get_high_mark(); } else throw SRC_BUG; // 'taille' is equal to zero (?) return taille; } void elastic::randomize(unsigned char *a) const { do { *a = (unsigned char)(rand() % 256); } while(*a == SINGLE_MARK || *a == get_low_mark() || *a == get_high_mark()); } U_I elastic::base_from_version(const archive_version & reading_ver) const { if(reading_ver > 6) return 254; else return 256; } unsigned char elastic::get_low_mark(const archive_version & reading_ver) const { if(reading_ver > 6) return get_low_mark(); else return '>'; } unsigned char elastic::get_high_mark(const archive_version & reading_ver) const { if(reading_ver > 6) return get_high_mark(); else return '<'; } } // end of namespace dar-2.7.15/src/libdar/erreurs.cpp0000644000175000017500000001205714636066467013543 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STDLIB_H #include #endif #if HAVE_EXECINFO_H #include #endif } // end extern "C" #include #include "erreurs.hpp" #include "infinint.hpp" #include "deci.hpp" #include "tools.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { static bool initialized = false; // does not avoid thread safe library. // if two thread act at the same time on this // value, this would have the same result // as if only one had acted on it. // subsequent action are read-only (once initialized is true). static void init(); static void notcatched(); const char *dar_gettext(const char *arg) { const char *ret = nullptr; NLS_SWAP_IN; try { ret = gettext(arg); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } const std::string Egeneric::empty_string = ""; Egeneric::Egeneric(const string &source, const string &message) { if(!initialized) init(); pile.push_front(niveau(source, message)); } const string & Egeneric::find_object(const string & location) const { deque::const_iterator it = pile.begin(); while(it != pile.end() && it->lieu != location) it++; if(it == pile.end()) return empty_string; else return it->objet; } void Egeneric::prepend_message(const std::string & context) { if(pile.empty()) throw SRC_BUG; pile.front().objet = context + pile.front().objet; } string Egeneric::dump_str() const { string ret; deque::const_iterator it = pile.begin(); ret += "---- exception type = [" + exceptionID() + "] ----------\n"; ret += "[source]\n"; while(it != pile.end()) { ret += "\t" + it->lieu + " : " + it->objet + "\n"; it++; } ret += "[most outside call]\n"; ret += "-----------------------------------\n\n"; return ret; } Ebug::Ebug(const string & file, S_I line) : Egeneric(tools_printf(gettext("File %S line %d"), &file, line), gettext("it seems to be a bug here")) { // adding the current stack if possible #if BACKTRACE_AVAILABLE const int buf_size = 20; void *buffer[buf_size]; int size = backtrace(buffer, buf_size); char **symbols = backtrace_symbols(buffer, size); try { for(int i = 0; i < size; ++i) Egeneric::stack("stack dump", string(symbols[i])); } catch(...) { if(symbols != nullptr) free(symbols); throw; } if(symbols != nullptr) free(symbols); #else Egeneric::stack("stack dump", "backtrace() call absent, cannot dump the stack information at the time the exception was thrown"); #endif } void Ebug::stack(const string & passage, const string & file, const string & line) { Egeneric::stack(passage, tools_printf(gettext("in file %S line %S"), &file, &line)); } Esystem::Esystem(const string & source, const string & message, io_error code): Egeneric(source, message) { x_code = code; } static void init() { set_terminate(notcatched); initialized = true; } static void notcatched() { cerr << "###############################################" << endl; cerr << gettext("# NOT CAUGHT EXCEPTION, #") << endl; cerr << gettext("# E X I T I N G ! #") << endl; cerr << "# #" << endl; cerr << "###############################################" << endl; cerr << tools_printf(gettext(" THANKS TO REPORT THE PREVIOUS OUTPUT TO MAINTAINER\n GIVING A DESCRIPTION OF THE CIRCUMSTANCES.")) << endl; cerr << tools_printf(gettext(" IF POSSIBLE TRY TO PRODUCE THIS ERROR, A\n SCENARIO THAT CAN REPRODUCE IT WOULD HELP MUCH\n IN SOLVING THIS PROBLEM. THANKS")) < #endif } #include "bzip2_module.hpp" #include "int_tools.hpp" #include "tools.hpp" using namespace std; namespace libdar { bzip2_module::bzip2_module(U_I compression_level) { #if LIBBZ2_AVAILABLE if(compression_level > 9 || compression_level < 1) throw Erange("bzip2_module::bzip2_module", tools_printf(gettext("out of range BZIP2 compression level: %d"), compression_level)); level = compression_level; #else throw Ecompilation(gettext("bzip2 compression")); #endif } U_I bzip2_module::get_max_compressing_size() const { #if LIBBZ2_AVAILABLE U_I unused = 0; return int_tools_maxof_aggregate(unused); #else throw Ecompilation(gettext("bzip2 compression")); #endif } U_I bzip2_module::get_min_size_to_compress(U_I clear_size) const { #if LIBBZ2_AVAILABLE if(clear_size > get_max_compressing_size() || clear_size < 1) throw Erange("bzip2_module::get_min_size_to_compress", "out of range block size submitted to bzip2_module::get_min_size_to_compress"); // extract for libzip2 manual: // * To guarantee that the compressed data will fit in its buffer, // * allocate an output buffer of size 1% larger than the uncompressed data, // * plus six hundred extra bytes. return clear_size + (clear_size+100)/100 + 600; // "(clear_size+100)/100" stands for "clear_size/100" (thus 1% of clear_size) // but rounded to the upper percent value #else throw Ecompilation(gettext("bzip2 compression")); #endif } U_I bzip2_module::compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const { #if LIBBZ2_AVAILABLE S_I ret; unsigned int tmp_zip_buf_size = zip_buf_size; // needed for long/non-long int conversion if(normal_size > get_max_compressing_size()) throw Erange("bzip2_module::compress_data", "oversized uncompressed data given to BZIP2 compression engine"); ret = BZ2_bzBuffToBuffCompress(zip_buf, & tmp_zip_buf_size, const_cast(normal), normal_size, level, 0, 30); // 30 is the default "workFactor" switch(ret) { case BZ_OK: break; case BZ_CONFIG_ERROR: throw Erange("bzip2_module::uncompress_data", "libbzip2 error: \"the library has been mis-compiled\""); case BZ_PARAM_ERROR: throw SRC_BUG; // zip, or zip_size is NULL or other parameter is out of range case BZ_MEM_ERROR: throw Erange("bzip2_module::uncompress_data", "lack of memory to perform the bzip2 compression operation"); case BZ_OUTBUFF_FULL: throw Erange("bzip2_module::uncompress_data", "too small buffer provided to receive compressed data"); default: throw SRC_BUG; } return tmp_zip_buf_size; #else throw Ecompilation(gettext("bzip2 compression")); #endif } U_I bzip2_module::uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const { #if LIBBZ2_AVAILABLE unsigned int tmp_normal_size = normal_size; // needed for long/non-long int conversion S_I ret = BZ2_bzBuffToBuffDecompress(normal, &tmp_normal_size, const_cast(zip_buf), zip_buf_size, 0, // small 0); // verbosity switch(ret) { case BZ_OK: break; case BZ_CONFIG_ERROR: throw Erange("bzip2_module::uncompress_data", "libbzip2 error: \"the library has been mis-compiled\""); case BZ_PARAM_ERROR: throw SRC_BUG; // normal, or normal_size is NULL or other parameter is out of range case BZ_MEM_ERROR: throw Erange("bzip2_module::uncompress_data", "lack of memory to perform the bzip2 decompression operation"); case BZ_OUTBUFF_FULL: throw Erange("bzip2_module::uncompress_data", "too small buffer provided to receive decompressed data"); case BZ_DATA_ERROR: case BZ_DATA_ERROR_MAGIC: case BZ_UNEXPECTED_EOF: throw Edata(gettext("corrupted compressed data met")); default: throw SRC_BUG; } return tmp_normal_size; #else throw Ecompilation(gettext("bzip2 compression")); #endif } unique_ptr bzip2_module::clone() const { #if LIBBZ2_AVAILABLE try { return std::make_unique(*this); } catch(bad_alloc &) { throw Ememory("bzip2_module::clone"); } #else throw Ecompilation(gettext("bzip2 compression")); #endif } } // end of namespace dar-2.7.15/src/libdar/generic_file.hpp0000644000175000017500000003717114636066467014500 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file generic_file.hpp /// \brief class generic_file is defined here as well as class fichier /// \ingroup Private /// /// the generic_file interface is widely used in libdar /// it defines the standard way of transmitting data between different /// part of the library /// - compression engine /// - encryption engine /// - exchange through pipes /// - slicing /// - dry run operations /// - file access /// . #ifndef GENERIC_FILE_HPP #define GENERIC_FILE_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "proto_generic_file.hpp" #include "integers.hpp" #include "crc.hpp" #include "infinint.hpp" #include "gf_mode.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// this is the interface class from which all other data transfer classes inherit /// it provides mainly read and write operations, /// skip operations and few other functions. /// \note /// the read and write method are similar to the read and write system calls /// except that they never return negative values, but throw exception instead. /// returning zero means end of generic_file. The call is blocked if /// no data is available for reading. /// write returns the number of bytes written, and never make partial writtings. /// Thus, it is blocked until all bytes are written or occures an exception /// inconsequences the returned value is always the value of the argument /// "size". class generic_file: public proto_generic_file { public : /// main constructor generic_file(gf_mode m) { rw = m; terminated = no_read_ahead = false; enable_crc(false); checksum = nullptr; }; /// copy constructor generic_file(const generic_file &ref) { copy_from(ref); }; /// move constructor generic_file(generic_file && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; /// assignment operator generic_file & operator = (const generic_file & ref) { destroy(); copy_from(ref); return *this; }; /// move operator generic_file & operator = (generic_file && ref) noexcept { move_from(std::move(ref)); return *this; }; /// virtual destructor, /// \note this let inherited destructor to be called even from a generic_file pointer to an inherited class ~generic_file() noexcept(false) { destroy(); }; /// destructor-like call, except that it is allowed to throw exceptions void terminate(); /// compaire the object content (implies skipping and reading) /// /// \note this is a more simple method than diff() bool operator == (generic_file & ref); bool operator != (generic_file & ref) { return ! (*this == ref); }; /// retreive the openning mode for this object gf_mode get_mode() const { return rw; }; /// read ahead information /// /// \param[in] amount is the expected amount of data the caller will read, if zero is given the object shall prepare as much as possible data for reading until a skip request, write request or a new read_ahead request /// \note after sanity checks, the protected inherited_read_ahead() method is called void read_ahead(const infinint & amount); /// ignore read ahead requests /// /// \param [in] mode if set to true read_ahead requests are ignored: inherited_read_ahead() of the inherited class /// is not called /// \note read_ahead is useful in multi-thread environement when a slave thread can work in advanced and asynchronously /// to provide read content to reader thread. However, read_ahead is a waste of CPU cycle for in a single threading model void ignore_read_ahead(bool mode) { no_read_ahead = mode; }; /// read data from the generic_file inherited from proto_generic_file virtual U_I read(char *a, U_I size) override; /// write data to the generic_file inherited from proto_generic_file virtual void write(const char *a, U_I size) override; /// write a string to the generic_file /// \note throws a exception if not all data could be written as expected void write(const std::string & arg); /// skip back one char, read on char and skip back one char S_I read_back(char &a); /// read one char S_I read_forward(char &a) { if(terminated) throw SRC_BUG; return read(&a, 1); }; enum skippability { skip_backward, skip_forward }; /// whether the implementation is able to skip /// \note the capability to skip does not mean that skip_relative() or /// skip() will succeed, but rather that the inherited class implementation /// does not by construction forbid the requested skip (like inherited class /// providing a generic_file interface of an anonymous pipe for example) virtual bool skippable(skippability direction, const infinint & amount) = 0; /// skip at the absolute position /// \param[in] pos the offset in byte where next read/write operation must start /// \return true if operation was successfull and false if the requested position is not valid (after end of file) /// \note if requested position is not valid the reading/writing cursor must be set to the closest valid position virtual bool skip(const infinint & pos) = 0; /// skip to the end of file virtual bool skip_to_eof() = 0; /// skip relatively to the current position virtual bool skip_relative(S_I x) = 0; /// whether the implementation is able to truncate to the given position virtual bool truncatable(const infinint & pos) const = 0; /// truncate file at the given offset /// \param[in] pos is the offset of the where to cut the file, in other word the pos - 1 will be the last byte of the /// file (if its size is greater than 1). If pos is greater than EOF the implementation should not enlarge the file /// and may even no skip the file to its current eof. Though this request is not considered an error implementation /// can thus do nothing when asked to truncate after EOF. /// \note running this call on a read-only object will always yeld an exception /// \note if current file position is after the point to truncate, implementation has to skip back the current writing /// offset to the new end of file. /// \note implementation of backward skippability and truncate operation should be coherent: If its not possible to /// skip backward it should not be possible to truncate the file to this position. If truncate a file to requested /// position is not possible, it should not be possible to skip backward to that position. This truncatable() with the /// skip_backward direction *should* indicate whether the truncate() action will succeed or generate an exception virtual void truncate(const infinint & pos); /// get the current read/write position virtual infinint get_position() const = 0; /// copy all data from current position to the object in argument virtual void copy_to(generic_file &ref); /// copy all data from the current position to the object in argument and computes a CRC value of the transmitted data /// \param[in] ref defines where to copy the data to /// \param[in] crc_size tell the width of the crc to compute on the copied data /// \param[out] value points to a newly allocated crc object containing the crc value /// \note value has to be deleted by the caller when no more needed virtual void copy_to(generic_file &ref, const infinint & crc_size, crc * & value); /// small copy (up to 4GB) with CRC calculation U_32 copy_to(generic_file &ref, U_32 size); // returns the number of byte effectively copied /// copy the given amount to the object in argument infinint copy_to(generic_file &ref, infinint size); // returns the number of byte effectively copied /// compares the contents with the object in argument /// \param[in] f is the file to compare the current object with /// \param[in] me_read_ahead is the amount of data to read ahead from "*this" (0 for no limit, up to end of eof) /// \param[in] you_read_ahead is the amount of data to read ahead from f (0 for no limit, up to end of file /// \param[in] crc_size is the width of the CRC to use for calculation /// \param[out] value is the computed checksum, its value can be used for additional /// testing if this method returns false (no difference between files). The given checksum /// has to be set to the expected width by the caller. /// \return true if arg differ from "this" /// \note value has to be deleted by the caller when no more needed /// \note the more simple operator == method may be prefered to diff() when no crc calculation is required bool diff(generic_file & f, const infinint & me_read_ahead, const infinint & you_read_ahead, const infinint & crc_size, crc * & value); /// compare the contents with the object in argument, also providing the offset of the first difference met /// \param[in] f is the file to compare the current object with /// \param[in] me_read_ahead is the amount of data to read ahead from "*this" (0 for no limit, up to end of eof) /// \param[in] you_read_ahead is the amount of data to read ahead from f (0 for no limit, up to end of file /// \param[in] crc_size is the width of the CRC to use for calculation /// \param[out] value is the computed checksum, its value can be used for additional /// \param[out] err_offset in case of difference, holds the offset of the first difference met /// testing if this method returns false (no difference between files). The given checksum /// has to be set to the expected width by the caller. /// \return true if arg differ from "this", else false is returned and err_offset is set /// \note value has to be deleted by the caller when no more needed /// \note the more simple operator == method may be prefered to diff() when no crc calculation is required bool diff(generic_file & f, const infinint & me_read_ahead, const infinint & you_read_ahead, const infinint & crc_size, crc * & value, infinint & err_offset); /// reset CRC on read or writen data /// \param[in] width is the width to use for the CRC void reset_crc(const infinint & width); /// to known whether CRC calculation is activated or not bool crc_status() const { return active_read == &generic_file::read_crc; }; /// get CRC of the transfered date since last reset /// \return a newly allocated crc object, that the caller has the responsibility to delete /// \note does also ends checksum calculation, which if needed again /// have to be re-enabled calling reset_crc() method crc *get_crc(); /// write any pending data void sync_write(); /// be ready to read at current position, reseting all pending data for reading, cached and in compression engine for example void flush_read(); protected : void set_mode(gf_mode x) { rw = x; }; /// tells the object that several calls to read() will follow to probably obtain at least the given amount of data /// \param[in] amount is the maximum expected amount of data that is known to be read /// \note this call may be implemented as a do-nothing call, its presence is only /// to allow optimization when possible, like in multi-threaded environment virtual void inherited_read_ahead(const infinint & amount) = 0; /// implementation of read() operation /// \param[in,out] a where to put the data to read /// \param[in] size says how much data to read /// \return the exact amount of data read and put into 'a' /// \note read as much byte as requested, up to end of file /// stays blocked if not enough data is available and EOF not /// yet met. May return less data than requested only if EOF as been reached. /// in other worlds, EOF is reached when returned data is stricly less than the requested data /// Any problem shall be reported by throwing an exception. virtual U_I inherited_read(char *a, U_I size) = 0; /// implementation of the write() operation /// \param[in] a what data to write /// \param[in] size amount of data to write /// \note must either write all data or report an error by throwing an exception virtual void inherited_write(const char *a, U_I size) = 0; /// truncate file at the give offset /// \note if pos is greater than the current file size, this call may do nothing (not even enlarging the file) /// \note this call should always fail on a read-only generic_file /// \note implementation must throw exception if truncate is not possible for other reason than /// read/write access mode virtual void inherited_truncate(const infinint & pos) = 0; /// write down any pending data /// \note called after sanity checks from generic_file::sync_write() /// this method's role is to write down any data pending for writing in the current object /// it has not to be propagated to other gneric_file object this object could rely on virtual void inherited_sync_write() = 0; /// reset internal engine, flush caches in order to read the data at current position /// \note when the object relies on external object or system object to fetch the data from for reading, /// when a call to (inherited_)flush_read() occurs, the current object must not assume that any previously read /// data is still valid if it has internal buffers or the like and it should flush them asap. This call must /// not propagate the flush_read to any other gneric_file object it could rely on virtual void inherited_flush_read() = 0; /// destructor-like call, except that it is allowed to throw exceptions /// \note this method must never be called directly but using terminate() instead, /// generic_file class manages it to never be called more than once virtual void inherited_terminate() = 0; /// is some specific call (skip() & Co.) need to be forbidden when the object /// has been terminated, one can use this call to check the terminated status bool is_terminated() const { return terminated; }; private : gf_mode rw; crc *checksum; bool terminated; bool no_read_ahead; U_I (generic_file::* active_read)(char *a, U_I size); void (generic_file::* active_write)(const char *a, U_I size); void enable_crc(bool mode); U_I read_crc(char *a, U_I size); void write_crc(const char *a, U_I size); void destroy(); void nullifyptr() noexcept { checksum = nullptr; }; void copy_from(const generic_file & ref); void move_from(generic_file && ref) noexcept; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/etage.cpp0000644000175000017500000001567014636067146013140 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // this was necessary to compile under Mac OS-X (boggus dirent.h) #if HAVE_STDINT_H #include #endif #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "etage.hpp" #include "tools.hpp" #include "infinint.hpp" #include "generic_file.hpp" #include "cygwin_adapt.hpp" #include "user_interaction.hpp" #include "fichier_local.hpp" #if defined (__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 23) #define CAN_USE_READDIR_R 0 #else #define CAN_USE_READDIR_R 1 #endif #define CACHE_DIR_TAG_FILENAME "CACHEDIR.TAG" #define CACHE_DIR_TAG_FILENAME_CONTENTS "Signature: 8a477f597d28d172789f06886806bc55" using namespace std; namespace libdar { // check if the given file is a tag telling if the current directory is a cache directory static bool cache_directory_tagging_check(const char *cpath, const char *filename); etage::etage(user_interaction &ui, const char *dirname, const datetime & x_last_acc, const datetime & x_last_mod, bool cache_directory_tagging, bool furtive_read_mode) { struct dirent *ret; DIR *tmp = nullptr; #if FURTIVE_READ_MODE_AVAILABLE int fddir = -1; if(furtive_read_mode) { fddir = ::open(dirname, O_RDONLY|O_BINARY|O_NOATIME); if(fddir < 0) { if(errno != EPERM) throw Erange("etage::etage", string(gettext("Error opening directory in furtive read mode: ")) + dirname + " : " + tools_strerror_r(errno)); else // using back normal access mode { string tmp = tools_strerror_r(errno); ui.message(tools_printf(gettext("Could not open directory %s in furtive read mode (%s), using normal mode"), dirname, tmp.c_str())); } } } #endif try { #if FURTIVE_READ_MODE_AVAILABLE if(furtive_read_mode && fddir >= 0) { tmp = fdopendir(fddir); if(tmp == nullptr) close(fddir); // else, tmp != nullptr: // fddir is now under control of the system // we must not close it. } else tmp = opendir(dirname); #else tmp = opendir(dirname); #endif bool is_cache_dir = false; if(tmp == nullptr) throw Erange("etage::etage" , string(gettext("Error opening directory: ")) + dirname + " : " + tools_strerror_r(errno)); fichier.clear(); while(!is_cache_dir && (ret = readdir(tmp)) != nullptr) { if(strcmp(ret->d_name, ".") != 0 && strcmp(ret->d_name, "..") != 0) { if(cache_directory_tagging) is_cache_dir = cache_directory_tagging_check(dirname, ret->d_name); fichier.push_back(string(ret->d_name)); } } closedir(tmp); tmp = nullptr; if(is_cache_dir) { fichier.clear(); ui.message(tools_printf(gettext("Detected Cache Directory Tagging Standard for %s, the contents of that directory will not be saved"), dirname)); // drop all the contents of the directory because it follows the Cache Directory Tagging Standard } last_mod = x_last_mod; last_acc = x_last_acc; } catch(...) { if(tmp != nullptr) closedir(tmp); throw; } } bool etage::read(string & ref) { if(fichier.empty()) return false; else { ref = fichier.front(); fichier.pop_front(); return true; } } /////////////////////////////////////////// ////////////// static functions /////////// /////////////////////////////////////////// static bool cache_directory_tagging_check(const char *cpath, const char *filename) { bool ret = false; if(strcmp(CACHE_DIR_TAG_FILENAME, filename) != 0) ret = false; else // we need to inspect the few first bytes of the file { try { path chem = path(cpath).append(filename); fichier_local fic = fichier_local(chem.display(), false); U_I len = strlen(CACHE_DIR_TAG_FILENAME_CONTENTS); char *buffer = new (nothrow) char[len+1]; S_I lu; if(buffer == nullptr) throw Ememory("etage:cache_directory_tagging_check"); try { lu = fic.read(buffer, len); if(lu < 0 || (U_I)(lu) < len) ret = false; else ret = strncmp(buffer, CACHE_DIR_TAG_FILENAME_CONTENTS, len) == 0; } catch(...) { delete [] buffer; throw; } delete [] buffer; } catch(Ememory & e) { throw; } catch(Ebug & e) { throw; } catch(...) { ret = false; } } return ret; } } // end of namespace dar-2.7.15/src/libdar/cat_ignored.cpp0000644000175000017500000000260014636066467014323 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_ignored.hpp" using namespace std; namespace libdar { bool cat_ignored::operator == (const cat_entree & ref) const { const cat_ignored *ref_ignored = dynamic_cast(&ref); if(ref_ignored == nullptr) return false; else return cat_nomme::operator == (ref); } } // end of namespace dar-2.7.15/src/libdar/infinint.hpp0000644000175000017500000000274414636066467013701 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file infinint.hpp /// \brief switch module to limitint (32 ou 64 bits integers) or infinint /// \ingroup API #ifndef INFININT_HPP #define INFININT_HPP #ifndef LIBDAR_MODE #include "real_infinint.hpp" #else #if LIBDAR_MODE == 32 #define INFININT_BASE_TYPE U_32 #include "integers.hpp" #include "limitint.hpp" # else #if LIBDAR_MODE == 64 #define INFININT_BASE_TYPE U_64 #include "integers.hpp" #include "limitint.hpp" #else #include "real_infinint.hpp" #endif #endif #endif #endif dar-2.7.15/src/libdar/filesystem_backup.cpp0000644000175000017500000002336514636067146015564 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif #if HAVE_TIME_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_SYS_UN_H #include #endif #if HAVE_UNISTD_H #include #endif #if STDC_HEADERS #include #endif #ifdef LIBDAR_NODUMP_FEATURE #if HAVE_SYS_IOCTL_H #include #endif #if LIBDAR_NODUMP_FEATURE == NODUMP_LINUX #include #else #if LIBDAR_NODUMP_FEATURE == NODUMP_EXT2FS #include #else #error "unknown location of ext2_fs.h include file" #endif #endif #endif #if MAJOR_IN_MKDEV #include #if !defined(makedev) && defined(mkdev) #define makedev(a,b) mkdev((a),(b)) #endif #else #if MAJOR_IN_SYSMACROS #include #endif #endif } // end extern "C" #include #include #include "filesystem_backup.hpp" #include "tools.hpp" #include "filesystem_tools.hpp" #include "erreurs.hpp" #include "user_interaction.hpp" #include "cat_all_entrees.hpp" #include "ea_filesystem.hpp" #include "cygwin_adapt.hpp" #include "fichier_local.hpp" #include "generic_rsync.hpp" #include "null_file.hpp" using namespace std; namespace libdar { filesystem_backup::filesystem_backup(const std::shared_ptr & dialog, const path &root, bool x_info_details, const mask & x_ea_mask, bool check_no_dump_flag, bool x_alter_atime, bool x_furtive_read_mode, bool x_cache_directory_tagging, infinint & root_fs_device, bool x_ignore_unknown, const fsa_scope & scope): filesystem_hard_link_read(dialog, x_furtive_read_mode, scope) { fs_root = nullptr; current_dir = nullptr; ea_mask = nullptr; try { fs_root = filesystem_tools_get_root_with_symlink(*dialog, root, x_info_details); if(fs_root == nullptr) throw Ememory("filesystem_backup::filesystem_backup"); info_details = x_info_details; no_dump_check = check_no_dump_flag; alter_atime = x_alter_atime; furtive_read_mode = x_furtive_read_mode; cache_directory_tagging = x_cache_directory_tagging; current_dir = nullptr; ignore_unknown = x_ignore_unknown; ea_mask = x_ea_mask.clone(); if(ea_mask == nullptr) throw Ememory("filesystem_backup::filesystem_backup"); reset_read(root_fs_device); } catch(...) { detruire(); throw; } } void filesystem_backup::detruire() { if(fs_root != nullptr) { delete fs_root; fs_root = nullptr; } if(current_dir != nullptr) { delete current_dir; current_dir = nullptr; } if(ea_mask != nullptr) { delete ea_mask; ea_mask = nullptr; } } void filesystem_backup::reset_read(infinint & root_fs_device) { corres_reset(); if(current_dir != nullptr) delete current_dir; current_dir = new (nothrow) path(*fs_root); if(current_dir == nullptr) throw Ememory("filesystem_backup::reset_read"); pile.clear(); const string display = current_dir->display(); const char* tmp = display.c_str(); cat_entree *ref = make_read_entree(*current_dir, "", true, *ea_mask); cat_directory *ref_dir = dynamic_cast(ref); try { if(ref_dir != nullptr) { pile.push_back(etage(get_ui(), tmp, ref_dir->get_last_access(), ref_dir->get_last_modif(), cache_directory_tagging, furtive_read_mode)); root_fs_device = ref_dir->get_device(); } else if(ref == nullptr) throw Erange("filesystem_backup::reset_read", string(gettext("Non existent file: ")) + tmp); else throw Erange("filesystem_backup::reset_read", string(gettext("File must be a directory: "))+ tmp); } catch(...) { if(ref != nullptr) delete ref; throw; } if(ref != nullptr) delete ref; } bool filesystem_backup::read(cat_entree * & ref, infinint & errors, infinint & skipped_dump) { bool once_again; ref = nullptr; errors = 0; skipped_dump = 0; if(current_dir == nullptr) throw SRC_BUG; // constructor not called or badly implemented. do { once_again = false; if(pile.empty()) return false; // end of filesystem reading else // at least one directory to read { etage & inner = pile.back(); string name; if(!inner.read(name)) { string tmp; if(!alter_atime && !furtive_read_mode) tools_noexcept_make_date(current_dir->display(), false, inner.last_acc, inner.last_mod, inner.last_mod); pile.pop_back(); if(pile.empty()) return false; // end of filesystem else { if(! current_dir->pop(tmp)) throw SRC_BUG; ref = new (nothrow) cat_eod(); } } else // could read a filename in directory { try { // checking the EXT2 nodump flag (if set ignoring the file) if(!no_dump_check || !filesystem_tools_is_nodump_flag_set(get_ui(), *current_dir, name, info_details)) { ref = make_read_entree(*current_dir, name, true, *ea_mask); try { cat_directory *ref_dir = dynamic_cast(ref); if(ref_dir != nullptr) { *current_dir += name; const string display = current_dir->display(); const char* ptr_name = display.c_str(); try { pile.push_back(etage(get_ui(), ptr_name, ref_dir->get_last_access(), ref_dir->get_last_modif(), cache_directory_tagging, furtive_read_mode)); } catch(Egeneric & e) { string tmp; get_ui().message(tools_printf(gettext("Cannot read directory contents: %s : "), ptr_name) + e.get_message()); try { pile.push_back(etage()); } catch(Egeneric & e) { delete ref; ref = nullptr; // we ignore this directory and skip to the next entry errors++; if(! current_dir->pop(tmp)) throw SRC_BUG; } } } if(ref == nullptr) once_again = true; // the file has been removed between the time // the directory has been openned, and the time // we try to read it, so we ignore it. // this case is silently ignored and not counted } catch(...) { if(ref != nullptr) { delete ref; ref = nullptr; } throw; } } else // EXT2 nodump flag is set, and we must not consider such file for backup { if(info_details) get_ui().message(string(gettext("Ignoring file with NODUMP flag set: ")) + (current_dir->append(name)).display()); skipped_dump++; once_again = true; } } catch(Edata & e) { if(!ignore_unknown) get_ui().message(string(gettext("Error reading directory contents: ")) + e.get_message() + gettext(" . Ignoring file or directory")); once_again = true; } catch(Erange & e) { get_ui().message(string(gettext("Error reading directory contents: ")) + e.get_message() + gettext(" . Ignoring file or directory")); once_again = true; errors++; } } } } while(once_again); if(ref == nullptr) throw Ememory("filesystem_backup::read"); else return true; } void filesystem_backup::skip_read_to_parent_dir() { string tmp; if(pile.empty()) throw SRC_BUG; else { if(!alter_atime && !furtive_read_mode) tools_noexcept_make_date(current_dir->display(), false, pile.back().last_acc, pile.back().last_mod, pile.back().last_mod); pile.pop_back(); } if(! current_dir->pop(tmp)) throw SRC_BUG; } } // end of namespace dar-2.7.15/src/libdar/catalogue.hpp0000644000175000017500000003465414636067146014027 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file catalogue.hpp /// \brief here is defined the classe used to manage catalogue of archives /// \ingroup Private #ifndef CATALOGUE_HPP #define CATALOGUE_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "infinint.hpp" #include "path.hpp" #include "integers.hpp" #include "mask.hpp" #include "user_interaction.hpp" #include "label.hpp" #include "escape.hpp" #include "datetime.hpp" #include "cat_entree.hpp" #include "cat_nomme.hpp" #include "cat_directory.hpp" #include "mem_ui.hpp" #include "delta_sig_block_size.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the catalogue class which gather all objects contained in a give archive class catalogue: public mem_ui { public : catalogue(const std::shared_ptr & ui, const datetime & root_last_modif, const label & data_name); catalogue(const std::shared_ptr & ui, const pile_descriptor & pdesc, const archive_version & reading_ver, compression default_algo, bool lax, const label & lax_layer1_data_name, // ignored unless in lax mode, in lax mode unless it is a cleared label, forces the catalogue label to be equal to the lax_layer1_data_name for it be considered a plain internal catalogue, even in case of corruption bool only_detruit = false); // if set to true, only directories and detruit objects are read from the archive catalogue(const catalogue & ref) : mem_ui(ref), out_compare(ref.out_compare), in_place(ref.in_place) { partial_copy_from(ref); }; catalogue(catalogue && ref) = delete; catalogue & operator = (const catalogue &ref); catalogue & operator = (catalogue && ref) = delete; virtual ~catalogue() { detruire(); }; // reading methods. The reading is iterative and uses the current_read cat_directory pointer virtual void reset_read() const; // set the reading cursor to the beginning of the catalogue virtual void end_read() const; // set the reading cursor to the end of the catalogue virtual void skip_read_to_parent_dir() const; // skip all items of the current dir and of any subdir, the next call will return // next item of the parent dir (no cat_eod to exit from the current dir !) virtual bool read(const cat_entree * & ref) const; // sequential read (generates cat_eod) and return false when all files have been read virtual bool read_if_present(std::string *name, const cat_nomme * & ref) const; // pseudo-sequential read (reading a directory still // implies that following read are located in this subdirectory up to the next EOD) but // it returns false if no entry of this name are present in the current directory // a call with nullptr as first argument means to set the current dir the parent directory void remove_read_entry(std::string & name); // in the currently read directory, removes the entry which name is given in argument const cat_directory & get_current_reading_dir() const { if(current_read == nullptr) throw SRC_BUG; return *current_read; }; // remove from the catalogue all the entries that have not yet been read // by read(). virtual void tail_catalogue_to_current_read(); void reset_sub_read(const path &sub); // initialise sub_read to the given directory bool sub_read(user_interaction & ui, const cat_entree * &ref); // sequential read of the catalogue, ignoring all that // is not part of the subdirectory specified with reset_sub_read // the read include the inode leading to the sub_tree as well as the pending cat_eod // return true if the last read entry has already been read // and has not to be counted again. This is never the case for catalogue but may occur // with escape_catalogue (where from the 'virtual'). // last this method gives a valid result only if the last read() entry is a directory as // only directory may be read() twice. virtual bool read_second_time_dir() const { return false; }; // Additions methods. The addition is also iterative but uses its specific current_add directory pointer void reset_add(); /// catalogue extension routines for escape sequence // real implementation is only needed in escape_catalogue class, here there nothing to be done virtual void pre_add(const cat_entree *ref, const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_ea(const cat_entree *ref, const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_crc(const cat_entree *ref, const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_dirty( const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_ea_crc(const cat_entree *ref, const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_waste_mark(const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_failed_mark(const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_fsa(const cat_entree *ref, const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_fsa_crc(const cat_entree *ref, const pile_descriptor* dest = nullptr) const {}; virtual void pre_add_delta_sig(const pile_descriptor* dest = nullptr) const {}; virtual escape *get_escape_layer() const { return nullptr; }; virtual void drop_escape_layer() {}; void add(cat_entree *ref); // add at end of catalogue (sequential point of view) void re_add_in(const std::string &subdirname); // return into an already existing subdirectory for further addition void re_add_in_replace(const cat_directory &dir); // same as re_add_in but also set the properties of the existing directory to those of the given argument void add_in_current_read(cat_nomme *ref); // add in currently read directory const cat_directory & get_current_add_dir() const { if(current_add == nullptr) throw SRC_BUG; return *current_add; }; // Comparison methods. The comparision is here also iterative and uses its specific current_compare directory pointer void reset_compare() const; bool compare(const cat_entree * name, const cat_entree * & extracted) const; // returns true if the ref exists, and gives it back in second argument as it is in the current catalogue. // returns false is no entry of that nature exists in the catalogue (in the current directory) // if ref is a directory, the operation is normaly relative to the directory itself, but // such a call implies a chdir to that directory. thus, a call with an EOD is necessary to // change to the parent directory. // note : // if a directory is not present, returns false, but records the inexistant subdirectory // structure defined by the following calls to this routine, this to be able to know when // the last available directory is back the current one when changing to parent directory, // and then proceed with normal comparison of inode. In this laps of time, the call will // always return false, while it temporary stores the missing directory structure // non interative methods /// add into "this" detruit object corresponding to object of ref absent in "this" ///\note ref must have the same directory tree "this", else the operation generates an exception infinint update_destroyed_with(const catalogue & ref); /// copy from ref missing files in "this" and mark then as "not_saved" (no change since reference) /// in case of abortion, completes missing files as if what could not be /// inspected had not changed since the reference was done /// aborting_last_etoile is the highest etoile reference withing "this" current object. void update_absent_with(const catalogue & ref, infinint aborting_next_etoile); /// remove/destroy from "this" all objects that are neither directory nor detruit objects void drop_all_non_detruits(); /// check whether all inode existing in the "this" and ref have the same attributes /// \note stops at the first inode found in both catalogue that do not match for at least one attribute /// including CRC for DATA, EA or FSA if present, then return false. bool is_subset_of(const catalogue & ref) const; /// before dumping the catalogue, need to set all hardlinked inode they have not been saved once void reset_dump() const; /// write down the whole catalogue to file void dump(const pile_descriptor & pdesc) const; entree_stats get_stats() const { return stats; }; /// whether the catalogue is empty or not bool is_empty() const { if(contenu == nullptr) throw SRC_BUG; return contenu->is_empty(); }; const cat_directory *get_contenu() const { return contenu; }; // used by data_tree const label & get_data_name() const { return ref_data_name; }; void set_data_name(const label & val) { ref_data_name = val; }; datetime get_root_dir_last_modif() const { return contenu->get_last_modif(); }; /// recursive evaluation of directories that have changed (make the cat_directory::get_recurisve_has_changed() method of entry in this catalogue meaningful) void launch_recursive_has_changed_update() const { contenu->recursive_has_changed_update(); }; /// recursive setting of mirage inode_wrote flag void set_all_mirage_s_inode_wrote_field_to(bool val) const { const_cast(contenu)->set_all_mirage_s_inode_wrote_field_to(val); }; datetime get_root_mtime() const { return contenu->get_last_modif(); }; /// reset all pointers to the root (a bit better than reset_add() + reset_read() + reset_compare() + reset_sub_read()) void reset_all(); void set_to_unsaved_data_and_FSA() { if(contenu == nullptr) throw SRC_BUG; contenu->recursively_set_to_unsaved_data_and_FSA(); }; /// change location where to find EA, FSA and DATA for all the objects of the catalogue void change_location(const pile_descriptor & pdesc); /// copy delta signatures to the given stack and update the cat_file objects accordingly /// \param[in] destination where to drop delta signatures /// \param[in] sequential_read whether we read the archive in sequential mode /// \param[in] build if set and delta signature is not present but data is available for a file, calculate the delta sig /// \param[in] delta_mask defines what files to calculate delta signature for when build is set to true /// \param[in] delta_sig_min_size minimum size below which to never calculate delta signatures /// \param[in] signature_block_size block size to use for computing delta signatures /// \note this method relies on reset_read() and read() void transfer_delta_signatures(const pile_descriptor & destination, bool sequential_read, bool build, const mask & delta_mask, const infinint & delta_sig_min_size, const delta_sig_block_size & signature_block_size); /// remove delta signature from the catalogue object as if they had never been calculated void drop_delta_signatures(); /// returns whether an in-place path is stored in the catalogue bool has_in_place() const { return in_place.is_absolute(); }; /// get the in_place path when available /// \param[out] arg value of the in_place path /// \return true if the catalogue has a valid in_place value /// else false is returned an the argument is undefined bool get_in_place(path & arg) const; /// set the in_place path for recording in the archive virtual void set_in_place(const path & arg); /// clear the in_place path virtual void clear_in_place(); protected: entree_stats & access_stats() { return stats; }; void copy_detruits_from(const catalogue & ref); // needed for escape_catalogue implementation only. const cat_eod * get_r_eod_address() const { return & r_eod; }; // cat_eod are never stored in the catalogue // however it is sometimes required to return such a reference to a valid object // owned by the catalogue. /// invert the data tree memory management responsibility pointed to by "contenu" pointers between the current /// catalogue and the catalogue given in argument. void swap_stuff(catalogue & ref); private : cat_directory *contenu; ///< catalogue contents mutable path out_compare; ///< stores the missing directory structure, when extracting mutable cat_directory *current_compare; ///< points to the current directory when extracting mutable cat_directory *current_add; ///< points to the directory where to add the next file with add_file; mutable cat_directory *current_read; ///< points to the directory where the next item will be read path *sub_tree; ///< path to sub_tree mutable signed int sub_count; ///< count the depth in of read routine in the sub_tree entree_stats stats; ///< statistics catalogue contents label ref_data_name; ///< name of the archive where is located the data path in_place; ///< path of the directory used for root of the backup (at the time of the backup) void partial_copy_from(const catalogue &ref); void detruire(); static const cat_eod r_eod; ///< needed to return eod reference, without taking risk of saturating memory static const U_I CAT_CRC_SIZE; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/path.hpp0000644000175000017500000001700714636066467013015 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file path.hpp /// \brief here is the definition of the path class /// \ingroup API /// /// the path class handle path and provide several operation on them #ifndef PATH_HPP #define PATH_HPP #include "../my_config.h" #include #include #include "integers.hpp" #include "erreurs.hpp" namespace libdar { /// \addtogroup API /// @{ /// the class path is here to manipulate paths in the Unix notation: using'/' /// several operations are provided as well as convertion functions, but /// for the API user, it can be used as if it was a std::string object. /// However if the argument is not a valid path, an exception may be thrown /// by libdar /// \note the most frequent method you will use from API point of view /// is path::display() which provides a std::string representing the path class path { public : /// constructor from a string /// This realizes the string to path convertion function /// \param[in] s the string to convert to path /// \param[in] x_undisclosed do not split the given string, consider it as a single directory name, even if some '/' are found in it /// \note empty string is not a valid string (exception thrown) /// \note having undisclosed set to true, does not allow one to pop() right away, first push must be made. While having undisclosed set to false /// let the user pop() right away if the given string is composed of several path members ("member1/member2/member3" for example of path /// allow one to pop() three time, while in the same example setting undisclosed to true, allow one to pop() just once). path(const std::string & s, bool x_undisclosed = false); /// copy constructor path(const path & ref); /// move constructor path(path && ref) noexcept = default; /// assignment operator path & operator = (const path & ref); /// move operator path & operator = (path && ref) noexcept = default; /// destructor ~path() = default; /// comparison operator bool operator == (const path & ref) const; bool operator != (const path & ref) const { return !(*this == ref); }; /// get the basename of a path /// this function returns the basename that's it the right most member of a path std::string basename() const; /// reset the read_subdir operation /// reset for read_subdir. next call to read_subdir is the most global void reset_read() const { reading = dirs.begin(); }; /// sequentially read the elements that compose the path /// \param[out] r the next element of the path /// \return true if a next element could be read /// \note the reading starts at the root and ends with the basename of the path bool read_subdir(std::string & r) const; /// whether the path is relative or absolute (= start with a /) bool is_relative() const { return relative; }; /// whether the path is absolute or relative bool is_absolute() const { return !relative; }; /// whether the path has an undisclosed part at the beginning bool is_undisclosed() const { return undisclosed; }; /// remove and gives in argument the basename of the path /// \param[out] arg the basename of the path /// \return false if the operation was not possible (no sub-directory to pop) /// \note if the path is absolute the remaing value is '/' when no pop is anymore possible /// while it is the first component of the original path if the path was relative. /// a empty path is not a valide value bool pop(std::string & arg); /// remove and gives in argument the outer most member of the path /// \param[out] arg the value of the outer element of the path /// \return true if the pop_front operation was possible and arg could be set. /// \note removes and returns the first directory of the path, /// when just the basename is present returns false, if the path is absolute, /// the first call change it to relative (except if equal to "/" then return false) bool pop_front(std::string & arg); /// add a path to the current path. The added path *must* be a relative path /// \param[in] arg the relative path to add /// \return the resulting path, (the current object is not modified, where from the "const" qualifier) /// \note arg can be a string also, which is converted to a path on the fly path operator + (const path & arg) const { path tmp = *this; tmp += arg; return tmp; }; /// add a single sub-directory to the path path append(const std::string & sub) const { path tmp = *this; if(sub.find_first_of("/") != std::string::npos) throw SRC_BUG; tmp += sub; return tmp; }; /// add a path to the current path. The added path *must* be a relative path /// \param[in] arg the relative path to add /// \return the value of the current (modified) object: "*this". path & operator += (const path & arg); /// add a single sub-directory to the current path object path & operator += (const std::string & sub); /// test whether the current object is a subdir of the method's argument /// \param[in] p the path to test with /// \param[in] case_sensit whether the test must be in case sensitive manner or not bool is_subdir_of(const path & p, bool case_sensit) const; /// convert back a path to a string /// the returned string is the representation of the current object in Unix notation std::string display() const; /// display the path as a string but without the first member of the path /// \note if path is equal to root (the first member if relative of / if absolute) /// an empty string is returned std::string display_without_root() const; /// returns the number of member in the path /// \note a absolute path counts one more that its relative brother U_I degre() const { return dirs.size() + (relative ? 0 : 1); }; /// if the current object is an undisclosed path, tries to convert it back to normal path void explode_undisclosed() const; private : mutable std::list::const_iterator reading; std::list dirs; bool relative; bool undisclosed; void reduce(); void init(const std::string & chem, bool x_undisclosed); }; /// root name to use when archive operation does not use filesystem (archive testing for example) extern const std::string PSEUDO_ROOT; /// root path object based on PSEUDO_ROOT extern const path FAKE_ROOT; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/ea_filesystem.cpp0000644000175000017500000002460214636066467014704 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #ifdef EA_SUPPORT #if HAVE_SYS_TYPES_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_ATTR_XATTR_H && ! HAVE_SYS_XATTR_H #include #endif #if HAVE_SYS_XATTR_H #include #endif #endif } // end extern "C" #include #include "ea_filesystem.hpp" #include "ea.hpp" #include "tools.hpp" #define MSG_NO_EA_SUPPORT "Extended Attribute support not activated at compilation time" #if defined(ENOATTR) #define LIBDAR_NOATTR ENOATTR #elif defined(ENODATA) #define LIBDAR_NOATTR ENODATA #endif using namespace std; namespace libdar { #ifdef EA_SUPPORT // Wrapper functions for l*attr inline static ssize_t my_lgetxattr(const char *path, const char *name, void *value, size_t size); inline static int my_lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags); inline static ssize_t my_llistxattr(const char *path, char *list, size_t size); inline static int my_lremovexattr(const char *path, const char *name); static bool remove_ea(const string & name, const ea_attributs & val, const mask & filter); static ea_attributs * read_ea(const string & name, const mask & filter); static deque ea_filesystem_get_ea_list_for(const char *filename); #endif static bool write_ea(const string & chemin, const ea_attributs & val, const mask & filter); bool ea_filesystem_write_ea(const string & chemin, const ea_attributs & val, const mask & filter) { return write_ea(chemin, val, filter); } ea_attributs * ea_filesystem_read_ea(const string & name, const mask & filter) { #ifdef EA_SUPPORT return read_ea(name, filter); #else return nullptr; #endif } void ea_filesystem_clear_ea(const string &name, const mask & filter) { #ifdef EA_SUPPORT ea_attributs * eat = ea_filesystem_read_ea(name, filter); try { if(eat != nullptr) remove_ea(name, *eat, bool_mask(true)); } catch(...) { if(eat != nullptr) delete eat; throw; } if(eat != nullptr) delete eat; #else throw Efeature(gettext(MSG_NO_EA_SUPPORT)); #endif } bool ea_filesystem_has_ea(const string & name) { #ifdef EA_SUPPORT deque val = ea_filesystem_get_ea_list_for(name.c_str()); return ! val.empty(); #else return false; #endif } bool ea_filesystem_has_ea(const string & name, const ea_attributs & list, const mask & filter) { #ifdef EA_SUPPORT const char *p_name = name.c_str(); bool ret = false; deque val = ea_filesystem_get_ea_list_for(p_name); deque::iterator it = val.begin(); string tmp; while(it != val.end() && ! ret) { if(filter.is_covered(*it)) ret = list.find(*it, tmp); it++; } return ret; #else return false; #endif } static bool write_ea(const string & chemin, const ea_attributs & val, const mask & filter) { U_I num = 0; #ifdef EA_SUPPORT // avoid unused variable compilation warning when EA_SUPPORT is not set const char *p_chemin = chemin.c_str(); #endif string key, value; val.reset_read(); while(val.read(key, value)) { // doing this for each attribute if(!filter.is_covered(key)) continue; // silently skipping this EA // now, action ! #ifdef EA_SUPPORT if(my_lsetxattr(p_chemin, key.c_str(), value.c_str(), value.size(), 0) < 0) { string tmp = tools_strerror_r(errno); throw Erange("ea_filesystem write_ea", tools_printf(gettext("Error while adding EA %s : %s"), key.c_str(), tmp.c_str())); } else num++; #else throw Efeature(gettext(MSG_NO_EA_SUPPORT)); #endif } return num > 0; } #ifdef EA_SUPPORT static bool remove_ea(const string & chemin, const ea_attributs & val, const mask & filter) { U_I num = 0; const char *p_chemin = chemin.c_str(); string key, value; val.reset_read(); while(val.read(key, value)) { // doing this for each attribute if(!filter.is_covered(key)) continue; // silently skipping this EA // now, action ! const char *k = key.c_str(); if(my_lremovexattr(p_chemin, k) < 0) { string tmp = tools_strerror_r(errno); #ifdef LIBDAR_NOATTR if(errno != LIBDAR_NOATTR) #endif throw Erange("ea_filesystem write_ea", tools_printf(gettext("Error while removing %s : %s"), k, tmp.c_str())); } else num++; } return num > 0; } static ea_attributs * read_ea(const string & name, const mask & filter) { const char *n_ptr = name.c_str(); ea_attributs *ret = nullptr; deque ea_liste = ea_filesystem_get_ea_list_for(n_ptr); deque::iterator it = ea_liste.begin(); try { while(it != ea_liste.end()) { if(filter.is_covered(*it)) { const char *a_name = it->c_str(); const U_I MARGIN = 2; string ea_ent_key, ea_ent_value; S_64 taille = my_lgetxattr(n_ptr, a_name, nullptr, 0); char *value = nullptr; if(taille < 0) { string tmp = tools_strerror_r(errno); throw Erange("ea_filesystem read_ea", tools_printf(gettext("Error reading attribute %s of file %s : %s"), a_name, n_ptr, tmp.c_str())); } if(ret == nullptr) { ret = new (nothrow) ea_attributs(); if(ret == nullptr) throw Ememory("read_ea"); ret->clear(); } if(taille > 0) { value = new (nothrow) char[taille+MARGIN]; if(value == nullptr) throw Ememory("filesystem : read_ea_from"); try { taille = my_lgetxattr(n_ptr, a_name, value, taille+MARGIN); // if the previous call overflows the buffer this may need to SEGFAULT and so on. if(taille < 0) { string tmp = tools_strerror_r(errno); throw Erange("ea_filesystem read_ea", tools_printf(gettext("Error reading attribute %s of file %s : %s"), a_name, n_ptr, tmp.c_str())); } ea_ent_key = *it; ea_ent_value = string((char *)value, (char *)value+taille); ret->add(ea_ent_key, ea_ent_value); } catch(...) { delete [] value; throw; } delete [] value; } else // trivial case where the value has a length of zero { ea_ent_key = *it; ea_ent_value = string(""); ret->add(ea_ent_key, ea_ent_value); } } it++; } } catch(...) { if(ret != nullptr) { delete ret; ret = nullptr; } throw; } return ret; } static deque ea_filesystem_get_ea_list_for(const char *filename) { deque ret; const U_I MARGIN = 2; ssize_t taille = my_llistxattr(filename, nullptr, 0); char *liste = nullptr; if(taille < 0) { if(errno == ENOSYS || errno == ENOTSUP) return ret; string tmp = tools_strerror_r(errno); throw Erange("ea_filesystem_get_ea_list_for", tools_printf(gettext("Error retrieving EA list for %s : %s"), filename, tmp.c_str())); } liste = new (nothrow) char[taille+MARGIN]; if(liste == nullptr) throw Ememory("filesystem : get_ea_list_for"); try { S_64 cursor = 0; taille = my_llistxattr(filename, liste, taille+MARGIN); if(taille < 0) { string tmp = tools_strerror_r(errno); throw Erange("ea_filesystem_get_ea_list_for", tools_printf(gettext("Error retrieving EA list for %s : %s"), filename, tmp.c_str())); } while(cursor < taille) { ret.push_back(string(liste+cursor)); cursor += strlen(liste+cursor)+1; } } catch(...) { delete [] liste; throw; } delete [] liste; return ret; } #ifdef OSX_EA_SUPPORT inline static ssize_t my_lgetxattr(const char *path, const char *name, void *value, size_t size) { return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); } inline static int my_lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) { return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW | flags); } inline static ssize_t my_llistxattr(const char *path, char *list, size_t size) { return listxattr(path, list, size, XATTR_NOFOLLOW); } inline static int my_lremovexattr(const char *path, const char *name) { return removexattr(path, name, XATTR_NOFOLLOW); } #else inline static ssize_t my_lgetxattr(const char *path, const char *name, void *value, size_t size) { return lgetxattr(path, name, value, size); } inline static int my_lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) { return lsetxattr(path, name, value, size, flags); } inline static ssize_t my_llistxattr(const char *path, char *list, size_t size) { return llistxattr(path, list, size); } inline static int my_lremovexattr(const char *path, const char *name) { return lremovexattr(path, name); } #endif #endif } // end of namespace dar-2.7.15/src/libdar/database_options.hpp0000644000175000017500000002672014636066467015402 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file database_options.hpp /// \brief this file holds the options for database operations /// \ingroup API ///. class database_open_options ///. class database_dump_options ///. class database_add_options ///. class database_remove_options ///. class database_change_basename_options ///. class database_change_path_options ///. class database_restore_options ///. class database_used_options #ifndef DATABASE_OPTIONS_HPP #define DATABASE_OPTIONS_HPP #include "../my_config.h" #include #include namespace libdar { /// \addtogroup API /// @{ /// options to open a database class database_open_options { public: database_open_options() { clear(); }; database_open_options(const database_open_options & ref) = default; database_open_options(database_open_options && ref) noexcept = default; database_open_options & operator = (const database_open_options & ref) = default; database_open_options & operator = (database_open_options && ref) noexcept = default; ~database_open_options() = default; void clear() { x_partial = false; x_partial_read_only = false; x_warn_order = true; }; // setings /// partial option /// \param[in] value set to true to only load an manipulate database header /// \note if value is set to true, the database loading is quick but only some database methods are available (see the database class documentation) void set_partial(bool value) { x_partial = value; }; /// partial and read only option /// \param[in] value when set, the database is in partial mode *and* in read-only. It cannot be dumped or modified. /// \note if value is set to true, all restriction found for partial mode apply, and in addition, the database cannot be dumped (written back to file) /// \note partial_read_only implies partial, but partial does not imply partial_readonly (it can be dumped but modification /// can only take place in the archive header) void set_partial_read_only(bool value) { x_partial_read_only = value; if(value) x_partial = value; }; /// warning about file ordering in database /// \param[in] value whether to warn when file chronological ordering does not respect the order of archives void set_warn_order(bool value) { x_warn_order = value; }; // gettings bool get_partial() const { return x_partial; }; bool get_partial_read_only() const { return x_partial_read_only; }; bool get_warn_order() const { return x_warn_order; }; private: bool x_partial; bool x_partial_read_only; bool x_warn_order; }; /// options to write a database to file class database_dump_options { public: database_dump_options() { clear(); }; database_dump_options(const database_dump_options & ref) = default; database_dump_options(database_dump_options && ref) noexcept = default; database_dump_options & operator = (const database_dump_options & ref) = default; database_dump_options & operator = (database_dump_options && ref) noexcept = default; ~database_dump_options() = default; void clear() { x_overwrite = false; }; // settings /// overwrite option /// \param[in] value whether we can overwrite the file if it already exists /// void set_overwrite(bool value) { x_overwrite = value; }; // gettings bool get_overwrite() const { return x_overwrite; }; private: bool x_overwrite; }; /// options to add an archive to base /// for now this is a placeholder class class database_add_options { public: database_add_options() { clear(); }; database_add_options(const database_add_options & ref) = default; database_add_options(database_add_options && ref) noexcept = default; database_add_options & operator = (const database_add_options & ref) = default; database_add_options & operator = (database_add_options && ref) noexcept = default; ~database_add_options() = default; void clear() {}; }; /// options to remove an archive from the base class database_remove_options { public: database_remove_options() { clear(); }; database_remove_options(const database_remove_options & ref) = default; database_remove_options(database_remove_options && ref) noexcept = default; database_remove_options & operator = (const database_remove_options & ref) = default; database_remove_options & operator = (database_remove_options && ref) noexcept = default; ~database_remove_options() = default; void clear() { x_revert_archive_numbering = false; }; /// defines whether the archive number is counted from the beginning or from the end of the database void set_revert_archive_numbering(bool revert) { x_revert_archive_numbering = revert; }; bool get_revert_archive_numbering() const { return x_revert_archive_numbering; }; private: bool x_revert_archive_numbering; }; /// options for changing a given archive's basename class database_change_basename_options { public: database_change_basename_options() { clear(); }; database_change_basename_options(const database_change_basename_options & ref) = default; database_change_basename_options(database_change_basename_options && ref) noexcept = default; database_change_basename_options & operator = (const database_change_basename_options & ref) = default; database_change_basename_options & operator = (database_change_basename_options && ref) noexcept = default; ~database_change_basename_options() = default; void clear() { x_revert_archive_numbering = false; }; /// defines whether the archive number is counted from the beginning or from the end of the database void set_revert_archive_numbering(bool revert) { x_revert_archive_numbering = revert; }; bool get_revert_archive_numbering() const { return x_revert_archive_numbering; }; private: bool x_revert_archive_numbering; }; /// options for changing a given archive's path class database_change_path_options { public: database_change_path_options() { clear(); }; database_change_path_options(const database_change_path_options & ref) = default; database_change_path_options(database_change_path_options && ref) noexcept = default; database_change_path_options & operator = (const database_change_path_options & ref) = default; database_change_path_options & operator = (database_change_path_options && ref) noexcept = default; ~database_change_path_options() = default; void clear() { x_revert_archive_numbering = false; }; /// defines whether the archive number is counted from the beginning or from the end of the database void set_revert_archive_numbering(bool revert) { x_revert_archive_numbering = revert; }; bool get_revert_archive_numbering() const { return x_revert_archive_numbering; }; private: bool x_revert_archive_numbering; }; /// options for restoration from database class database_restore_options { public: database_restore_options() { clear(); }; database_restore_options(const database_restore_options & ref) = default; database_restore_options(database_restore_options && ref) noexcept = default; database_restore_options & operator = (const database_restore_options & ref) = default; database_restore_options & operator = (database_restore_options && ref) noexcept = default; ~database_restore_options() = default; void clear() { x_early_release = x_info_details = x_ignore_dar_options_in_database = x_even_when_removed = false; x_date = 0; x_extra_options_for_dar.clear(); }; // settings /// early_release option /// if early_release is set to true, some memory is released before calling dar /// \note if early_release is true, this will free almost all memory allocated by the database before calling dar. /// drawback is that no more action is possible after this call (except destruction) void set_early_release(bool value) { x_early_release = value; }; /// info_details option /// if set to true, more detailed informations is displayed void set_info_details(bool value) { x_info_details = value; }; /// extra options to dar /// this option is a mean to provide some extra options to dar from dar_manager void set_extra_options_for_dar(const std::vector & value) { x_extra_options_for_dar = value; }; /// ignore options to dar embedded in the database void set_ignore_dar_options_in_database(bool mode) { x_ignore_dar_options_in_database = mode; }; /// date option /// informations about files more recent than the given date are ignored. So you can restore file in the most recent state before a certain "date". /// \note if set to zero, the most recent state available is looked for (this is the default value). void set_date(const infinint & value) { x_date = value; }; /// find data or EA if they have been removed at the requested data /// in the case a file has was removed at the request date, the data or EA /// that will be restored will be the one of it had just before being removed void set_even_when_removed(bool value) { x_even_when_removed = value; }; // gettings bool get_early_release() const { return x_early_release; }; bool get_info_details() const { return x_info_details; }; const std::vector & get_extra_options_for_dar() const { return x_extra_options_for_dar; }; const infinint & get_date() const { return x_date; }; bool get_ignore_dar_options_in_database() const { return x_ignore_dar_options_in_database; }; bool get_even_when_removed() const { return x_even_when_removed; }; private: bool x_early_release; bool x_info_details; std::vector x_extra_options_for_dar; infinint x_date; bool x_ignore_dar_options_in_database; bool x_even_when_removed; }; /// options for file "used" in archive class database_used_options { public: database_used_options() { clear(); }; database_used_options(const database_used_options & ref) = default; database_used_options(database_used_options && ref) noexcept = default; database_used_options & operator = (const database_used_options & ref) = default; database_used_options & operator = (database_used_options && ref) noexcept = default; ~database_used_options() = default; void clear() { x_revert_archive_numbering = false; }; /// defines whether the archive number is counted from the beginning or from the end of the database void set_revert_archive_numbering(bool revert) { x_revert_archive_numbering = revert; }; bool get_revert_archive_numbering() const { return x_revert_archive_numbering; }; private: bool x_revert_archive_numbering; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/proto_tronco.hpp0000644000175000017500000000563514636066467014614 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file proto_tronco.hpp /// \brief defines common interface for tronconneuse and parallel_tronconneuse /// \ingroup Private /// #ifndef PROTO_TRONCO_HPP #define PROTO_TRONCO_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "integers.hpp" #include "generic_file.hpp" #include "archive_version.hpp" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the trailing_clear_data_callback call back is a mean by which the upper layer cat tell when encrypted data ends /// \param[in] below is the generic_file containing the encrypted data that the proto_tronco inherited /// class is willing to decipher. /// \param[in] reading_ver is the archive format version of the archive under operation /// \return the callback returns the offset of the first non encrypted data at the end of the provided generic_file. /// \note this method should be invoked when decryption failed at or near end of file. /// \note the libdar archive format is ended by a clear trailier which is expected /// to be read backward, by the end of the archive. The last part of this trailer (see terminateur class) records the offset of the /// beginning of this trailier (which are all clear clear), it is thus possible outside of the /// encrypted layer to tail the clear data from the encrypted one and avoid trying to decipher /// data that is not encrypted. This is the reason of existence for this callback. typedef infinint (*trailing_clear_data_callback)(generic_file & below, const archive_version & reading_ver); class proto_tronco: public generic_file { public: using generic_file::generic_file; virtual void set_initial_shift(const infinint & x) = 0; virtual void set_callback_trailing_clear_data(trailing_clear_data_callback) = 0; virtual U_32 get_clear_block_size() const = 0; virtual void write_end_of_file() = 0; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_tools.cpp0000644000175000017500000006707714636067146015467 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif #if HAVE_TIME_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_SYS_UN_H #include #endif #if HAVE_UNISTD_H #include #endif #if STDC_HEADERS #include #endif #ifdef LIBDAR_NODUMP_FEATURE #if HAVE_SYS_IOCTL_H #include #endif #if LIBDAR_NODUMP_FEATURE == NODUMP_LINUX #include #else #if LIBDAR_NODUMP_FEATURE == NODUMP_EXT2FS #include #else #error "unknown location of ext2_fs.h include file" #endif #endif #endif #if MAJOR_IN_MKDEV #include #if !defined(makedev) && defined(mkdev) #define makedev(a,b) mkdev((a),(b)) #endif #else #if MAJOR_IN_SYSMACROS #include #endif #endif } // end extern "C" #include #include #include "filesystem_tools.hpp" #include "erreurs.hpp" #include "generic_rsync.hpp" #include "null_file.hpp" #include "ea_filesystem.hpp" #include "cygwin_adapt.hpp" #include "etage.hpp" #include "tools.hpp" using namespace std; namespace libdar { bool filesystem_tools_has_immutable(const cat_inode & arg) { if(arg.fsa_get_saved_status() == fsa_saved_status::full) { const filesystem_specific_attribute_list *fsal = arg.get_fsa(); const filesystem_specific_attribute *fsa = nullptr; if(fsal == nullptr) throw SRC_BUG; if(fsal->find(fsaf_linux_extX, fsan_immutable, fsa)) { const fsa_bool *fsab = dynamic_cast(fsa); if(fsab == nullptr) throw SRC_BUG; return fsab->get_value(); } else return false; } else return false; } void filesystem_tools_set_immutable(const string & target, bool val, user_interaction &ui) { fsa_bool fsa(fsaf_linux_extX, fsan_immutable, val); filesystem_specific_attribute_list fsal; fsal.add(fsa); (void)fsal.set_fsa_to_filesystem_for(target, all_fsa_families(), ui, true); } void filesystem_tools_supprime(user_interaction & ui, const string & ref) { const char *s = ref.c_str(); struct stat buf; if(lstat(s, &buf) < 0) throw Erange("filesystem_tools_supprime", string(gettext("Cannot get inode information about file to remove ")) + s + " : " + tools_strerror_r(errno)); if(S_ISDIR(buf.st_mode)) { etage fils = etage(ui, s, datetime(0), datetime(0), false, false); // we don't care the access and modification time because directory will be destroyed string tmp; // first we destroy directory's children while(fils.read(tmp)) filesystem_tools_supprime(ui, (path(ref).append(tmp)).display()); // then the directory itself if(rmdir(s) < 0) throw Erange("filesystem_tools_supprime (dir)", string(gettext("Cannot remove directory ")) + s + " : " + tools_strerror_r(errno)); } else tools_unlink(s); } void filesystem_tools_widen_perm(user_interaction & dialog, const cat_inode & ref, const string & chem, comparison_fields what_to_check) { const cat_directory *ref_dir = dynamic_cast(&ref); S_I permission; const char *name = chem.c_str(); if(ref_dir == nullptr) return; // nothing to do if not a directory if(what_to_check != comparison_fields::all && what_to_check == comparison_fields::ignore_owner) return; // we do nothing if permission is not to take into account // for the operation // if we are not root (geteuid()!=0), we must try to have a chance to // be able to create/modify sub-files or sub-directory, so we set the user write access to // that directory. This chmod is allowed only if we own the directory (so // we only set the write bit for user). If this cannot be changed we are not the // owner of the directory, so we will try to restore as much as our permission // allows it (maybe "group" or "other" write bits are set for us). if(geteuid() != 0) { mode_t tmp; try { tmp = filesystem_tools_get_file_permission(name); // the current permission value } catch(Egeneric & e) { tmp = ref.get_perm(); // the value at the time of the backup if we failed reading permission from filesystem } permission = tmp | 0200; // add user write access to be able to add some subdirectories and files } else permission = ref.get_perm() | 0200; (void)chmod(name, permission); // we ignore if that failed maybe we will be more lucky // if for example group ownership gives us the right to // write in this existing directory } void filesystem_tools_make_owner_perm(user_interaction & dialog, const cat_inode & ref, const string & chem, comparison_fields what_to_check, const fsa_scope & scope) { const char *name = chem.c_str(); const cat_lien *ref_lie = dynamic_cast(&ref); // restoring fields that are defined by "what_to_check" // do we have to restore ownership? if(what_to_check == comparison_fields::all) { uid_t tmp_uid = 0; gid_t tmp_gid = 0; infinint tmp = ref.get_uid(); tmp.unstack(tmp_uid); if(!tmp.is_zero()) throw Erange("make_owner_perm", gettext("uid value is too high for this system for libdar be able to restore it properly")); tmp = ref.get_gid(); tmp.unstack(tmp_gid); if(!tmp.is_zero()) throw Erange("make_owner_perm", gettext("gid value is too high for this system for libdar be able to restore it properly")); #if HAVE_LCHOWN if(lchown(name, tmp_uid, tmp_gid) < 0) dialog.message(chem + string(gettext("Could not restore original file ownership: ")) + tools_strerror_r(errno)); #else if(dynamic_cast(&ref) == nullptr) // not a symbolic link if(chown(name, tmp_uid, tmp_gid) < 0) dialog.message(chem + string(gettext("Could not restore original file ownership: ")) + tools_strerror_r(errno)); // // we don't/can't restore ownership for symbolic links (no system call to do that) // #endif } // do we have to restore permissions? // note! permission must be restored after owner ship: // in case suid bits would be set, restoring ownership // would clear the suid bit, leading to improper restored // permission try { if(what_to_check == comparison_fields::all || what_to_check == comparison_fields::ignore_owner) if(ref_lie == nullptr) // not restoring permission for symbolic links, it would modify the target not the symlink itself { if(chmod(name, ref.get_perm()) < 0) { string tmp = tools_strerror_r(errno); dialog.message(tools_printf(gettext("Cannot restore permissions of %s : %s"), name, tmp.c_str())); } } } catch(Egeneric &e) { if(ref_lie == nullptr) throw; // else (the inode is a symlink), we simply ignore this error } } void filesystem_tools_make_date(const cat_inode & ref, const string & chem, comparison_fields what_to_check, const fsa_scope & scope) { const cat_lien *ref_lie = dynamic_cast(&ref); if(what_to_check == comparison_fields::all || what_to_check == comparison_fields::ignore_owner || what_to_check == comparison_fields::mtime) { datetime birthtime = ref.get_last_modif(); fsa_scope::iterator it = scope.find(fsaf_hfs_plus); if(ref.fsa_get_saved_status() == fsa_saved_status::full && it != scope.end()) { const filesystem_specific_attribute_list * fsa = ref.get_fsa(); const filesystem_specific_attribute *ptr = nullptr; if(fsa == nullptr) throw SRC_BUG; if(fsa->find(fsaf_hfs_plus, fsan_creation_date, ptr) || fsa->find(fsaf_linux_extX, fsan_creation_date, ptr)) { const fsa_time *ptr_time = dynamic_cast(ptr); if(ptr_time != nullptr) birthtime = ptr_time->get_value(); } } tools_make_date(chem, ref_lie != nullptr, ref.get_last_access(), ref.get_last_modif(), birthtime); } } void filesystem_tools_attach_ea(const string &chemin, cat_inode *ino, const mask & ea_mask) { ea_attributs *eat = nullptr; try { if(ino == nullptr) throw SRC_BUG; eat = ea_filesystem_read_ea(chemin, ea_mask); if(eat != nullptr) { if(eat->size() <= 0) throw SRC_BUG; ino->ea_set_saved_status(ea_saved_status::full); ino->ea_attach(eat); eat = nullptr; // allocated memory now managed by the cat_inode object } else ino->ea_set_saved_status(ea_saved_status::none); } catch(...) { if(eat != nullptr) delete eat; throw; } if(eat != nullptr) throw SRC_BUG; } bool filesystem_tools_is_nodump_flag_set(user_interaction & dialog, const path & chem, const string & filename, bool info) { #ifdef LIBDAR_NODUMP_FEATURE S_I fd, f = 0; const string display = (chem.append(filename)).display(); const char *ptr = display.c_str(); fd = ::open(ptr, O_RDONLY|O_BINARY|O_NONBLOCK); if(fd < 0) { if(info) { string tmp = tools_strerror_r(errno); dialog.message(tools_printf(gettext("Failed to open %S while checking for nodump flag: %s"), &filename, tmp.c_str())); } } else { try { if(ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) { if(errno != ENOTTY) { if(info) { string tmp = tools_strerror_r(errno); dialog.message(tools_printf(gettext("Cannot get ext2 attributes (and nodump flag value) for %S : %s"), &filename, tmp.c_str())); } } f = 0; } } catch(...) { close(fd); throw; } close(fd); } return (f & EXT2_NODUMP_FL) != 0; #else return false; #endif } path *filesystem_tools_get_root_with_symlink(user_interaction & dialog, const path & root, bool info_details) { path *ret = nullptr; const string display = root.display(); const char *ptr = display.c_str(); struct stat buf; if(lstat(ptr, &buf) < 0) // stat not lstat, thus we eventually get the symlink pointed to inode { string tmp = tools_strerror_r(errno); throw Erange("filesystem:get_root_with_symlink", tools_printf(gettext("Cannot get inode information for %s : %s"), ptr, tmp.c_str())); } if(S_ISDIR(buf.st_mode)) { ret = new (nothrow) path(root); if(ret == nullptr) throw Ememory("get_root_with_symlink"); } else if(S_ISLNK(buf.st_mode)) { ret = new (nothrow) path(tools_readlink(ptr)); if(ret == nullptr) throw Ememory("get_root_with_symlink"); if(ret->is_relative()) { string tmp; path base = root; if(base.pop(tmp)) *ret = base + *ret; else if(!root.is_relative()) throw SRC_BUG; // symlink name is not "popable" and is absolute, it is thus the filesystem root '/' // and it is a symbolic link !!! How is it possible that "/" be a symlink ? // a symlink to where ??? } if(info_details && ! (*ret == root) ) dialog.message(tools_printf(gettext("Replacing %s in the -R option by the directory pointed to by this symbolic link: "), ptr) + ret->display()); } else // not a directory given as argument throw Erange("filesystem:get_root_with_symlink", tools_printf(gettext("The given path %s must be a directory (or symbolic link to an existing directory)"), ptr)); if(ret == nullptr) throw SRC_BUG; // exit without exception, but ret not allocated ! return ret; } mode_t filesystem_tools_get_file_permission(const string & path) { struct stat buf; if(lstat(path.c_str(), &buf) < 0) { string tmp = tools_strerror_r(errno); throw Erange("filesystem.cpp:get_file_permission", tools_printf("Cannot read file permission for %s: %s",path.c_str(), tmp.c_str())); } return buf.st_mode; } void filesystem_tools_make_delta_patch(const shared_ptr & dialog, const cat_file & existing, const string & existing_pathname, const cat_file & patcher, const path & cur_directory) { infinint patch_crc_size = tools_file_size_to_crc_size(patcher.get_size()); infinint base_crc_size = tools_file_size_to_crc_size(existing.get_size()); crc * calculated_patch_crc = nullptr; ///< calculated CRC of the read patch data crc * calculated_base_crc = nullptr; ///< calculated CRC of the base file to be patched const crc *expected_patch_crc = nullptr; ///< expected CRC of the patched data const crc *expected_base_crc = nullptr; ///< expected CRC of the base file to be patched const crc *expected_result_crc = nullptr; ///< expected CRC of the resulting patched file string temporary_pathname; fichier_local *resulting = nullptr; ///< used to temporarily saved to patch result generic_file *current = nullptr; ///< will hold the data from the cat_file "existing" generic_file *delta = nullptr; ///< will hold the data from the cat_file "patcher" generic_rsync *rdiffer = nullptr; ///< interface to rsync to apply the patch null_file black_hole = gf_write_only; bool disable_base_check = patcher.get_archive_version() == archive_version(11,2); ///< workaround bug in that format // sanity checks if(!dialog) throw SRC_BUG; // dialog points to nothing if(existing.get_saved_status() != saved_status::saved) throw SRC_BUG; if(patcher.get_saved_status() != saved_status::delta) throw SRC_BUG; // try { try { // creating a temporary file to write the result of the patch to resulting = filesystem_tools_create_non_existing_file_based_on(dialog, existing.get_name(), cur_directory, temporary_pathname); if(resulting == nullptr) throw SRC_BUG; // we do not activate CRC at that time because // we have no clue of the resulting file size, thus // of the crc size to use // obtaining current file current = existing.get_data(cat_file::plain, nullptr, 0, nullptr); if(current == nullptr) throw SRC_BUG; else { if(! disable_base_check) { // calculating the crc of base file // note: this file will be read with a mix of skip() // by the generic_rsync object below, thus is is not // possible to calculate its CRC at tha time, so we // do it now for that reason current->reset_crc(base_crc_size); current->copy_to(black_hole); calculated_base_crc = current->get_crc(); if(calculated_base_crc == nullptr) throw SRC_BUG; current->skip(0); } } // obtaining patch delta = patcher.get_data(cat_file::plain, nullptr, 0, nullptr); if(delta == nullptr) throw SRC_BUG; else delta->reset_crc(patch_crc_size); // creating the patcher object (read-only object) // and checking the current data matches the expected_base_crc rdiffer = new (nothrow) generic_rsync(current, delta); if(rdiffer == nullptr) throw Ememory("filesystem_restore::make_delta_patch"); // patching the existing file to the resulting inode (which is a new file) rdiffer->copy_to(*resulting); rdiffer->terminate(); resulting->terminate(); // obtaining the expected CRC of the base file to patch if(!patcher.has_patch_base_crc()) throw SRC_BUG; // s_delta should have a ref CRC if(!patcher.get_patch_base_crc(expected_base_crc)) throw SRC_BUG; // has CRC true but fetching CRC failed! if(expected_base_crc == nullptr) throw SRC_BUG; // comparing the expected base crc with the calculated one if(!disable_base_check && *calculated_base_crc != *expected_base_crc) throw Erange("filesystem.cpp::make_delta_patch", gettext("File the patch is about to be applied to is not the expected one, aborting the patch operation")); // reading the calculated CRC of the patch data calculated_patch_crc = delta->get_crc(); if(calculated_patch_crc == nullptr) throw SRC_BUG; // checking the calculated CRC match the expected CRC for patch data if(patcher.get_crc(expected_patch_crc)) { if(expected_patch_crc == nullptr) throw SRC_BUG; if(*expected_patch_crc != *calculated_patch_crc) throw Erange("filesystem.cpp::make_delta_patch", gettext("Patch data does not match its CRC, archive corruption took place")); } else throw SRC_BUG; // at the archive format that support delta patch CRC is always present // reading the expected CRC of the resulting patched file // it will be provided for comparision with resulting data // when copying content from temporary file to destination file if(!patcher.has_patch_result_crc()) throw SRC_BUG; if(!patcher.get_patch_result_crc(expected_result_crc)) throw SRC_BUG; if(expected_result_crc == nullptr) throw SRC_BUG; // replacing the original source file by the resulting patched file. // doing that way to avoid loosing hard links toward that inode instead // of unlinking the old inode and rename the tempory to the name of the // original file try { filesystem_tools_copy_content_from_to(dialog, temporary_pathname, existing_pathname, expected_result_crc); } catch(Erange & e) { e.prepend_message(gettext("Error met while checking the resulting patched file: ")); throw; } } catch(...) { if(rdiffer != nullptr) delete rdiffer; if(delta != nullptr) delete delta; if(current != nullptr) delete current; if(resulting != nullptr) delete resulting; if(calculated_patch_crc != nullptr) delete calculated_patch_crc; if(calculated_base_crc != nullptr) delete calculated_base_crc; throw; } if(rdiffer != nullptr) delete rdiffer; if(delta != nullptr) delete delta; if(current != nullptr) delete current; if(resulting != nullptr) delete resulting; if(calculated_patch_crc != nullptr) delete calculated_patch_crc; if(calculated_base_crc != nullptr) delete calculated_base_crc; } catch(...) { try { tools_unlink(temporary_pathname); } catch(...) { // do nothing } throw; // propagate the original exception } tools_unlink(temporary_pathname); } fichier_local *filesystem_tools_create_non_existing_file_based_on(const std::shared_ptr & dialog, string filename, path where, string & new_filename) { const char *extra = "~#-%.+="; // a set of char that should be accepted in all filesystems fichier_local *ret = nullptr; U_I index = 0; do { if(!dialog) throw SRC_BUG; new_filename = filename + extra[++index]; where += new_filename; new_filename = where.display(); try { ret = new (nothrow) fichier_local(dialog, new_filename, gf_read_write, 0600, true, // fail_if_exists false, false); } catch(Esystem & e) { if(e.get_code() == Esystem::io_exist) { where.pop(new_filename); if(extra[index] == '\0') { index = 0; filename += string(extra, extra+1); } else ++index; } else throw; } } while(ret == nullptr); return ret; } void filesystem_tools_copy_content_from_to(const shared_ptr & dialog, const string & source_path, const string & destination_path, const crc *expected_crc) { if(!dialog) throw SRC_BUG; // dialog points to nothing fichier_local src = fichier_local(source_path); fichier_local dst = fichier_local(dialog, destination_path, gf_write_only, 0600, false, true, // erase false); if(expected_crc != nullptr) src.reset_crc(expected_crc->get_size()); src.copy_to(dst); if(expected_crc != nullptr) { crc * calculated_crc = src.get_crc(); if(calculated_crc == nullptr) throw SRC_BUG; try { if(*calculated_crc != *expected_crc) throw Erange("filesystem.cpp:copy_content_from_to", gettext("Copied data does not match expected CRC")); } catch(...) { if(calculated_crc != nullptr) delete calculated_crc; throw; } if(calculated_crc != nullptr) delete calculated_crc; } } bool filesystem_tools_read_linux_birthtime(const std::string & target, datetime & val) { #ifdef HAVE_STATX_SYSCALL struct statx value; int ret = statx(0, target.c_str(), 0, STATX_BTIME, &value); if(ret == 0) if((value.stx_mask & STATX_BTIME) != 0) { val = datetime(value.stx_btime.tv_sec, value.stx_btime.tv_nsec, datetime::tu_nanosecond); return true; } else return false; else return false; #else return false; #endif } } // end of namespace dar-2.7.15/src/libdar/data_tree.cpp0000644000175000017500000006660714636066467014016 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif } // end extern "C" #include #include "data_tree.hpp" #include "tools.hpp" #include "user_interaction.hpp" #include "deci.hpp" #include "path.hpp" #include "datetime.hpp" #include "cat_all_entrees.hpp" #include "candidates.hpp" using namespace std; namespace libdar { void data_tree::status::dump(generic_file & f) const { date.dump(f); switch(present) { case db_etat::et_saved: f.write(ETAT_SAVED, 1); break; case db_etat::et_patch: f.write(ETAT_PATCH, 1); break; case db_etat::et_patch_unusable: f.write(ETAT_PATCH_UNUSABLE, 1); break; case db_etat::et_inode: f.write(ETAT_INODE, 1); break; case db_etat::et_present: f.write(ETAT_PRESENT, 1); break; case db_etat::et_removed: f.write(ETAT_REMOVED, 1); break; case db_etat::et_absent: f.write(ETAT_ABSENT, 1); break; default: throw SRC_BUG; } } void data_tree::status::read(generic_file & f, unsigned char db_version) { char tmp; date.read(f, db2archive_version(db_version)); if(f.read(&tmp, 1) != 1) throw Erange("data_tree::status::read", gettext("reached End of File before all expected data could be read")); switch(tmp) { case ETAT_SAVED[0]: present = db_etat::et_saved; break; case ETAT_PRESENT[0]: present = db_etat::et_present; break; case ETAT_REMOVED[0]: present = db_etat::et_removed; break; case ETAT_INODE[0]: present = db_etat::et_inode; break; case ETAT_ABSENT[0]: present = db_etat::et_absent; break; case ETAT_PATCH[0]: present = db_etat::et_patch; break; case ETAT_PATCH_UNUSABLE[0]: present = db_etat::et_patch_unusable; break; default: throw Erange("data_tree::status::read", gettext("Unexpected value found in database")); } } data_tree::status_plus::status_plus(const datetime & d, db_etat p, const crc *xbase, const crc *xresult): status(d, p) { base = result = nullptr; try { if(xbase != nullptr) { base = xbase->clone(); if(base == nullptr) throw Ememory("data_tree::status_plus::status_plus"); } if(xresult != nullptr) { result = xresult->clone(); if(result == nullptr) throw Ememory("data_tree::status_plus::status_plus"); } } catch(...) { if(base != nullptr) delete base; if(result != nullptr) delete result; throw; } } void data_tree::status_plus::dump(generic_file & f) const { unsigned char flag = 0; if(base != nullptr) flag |= STATUS_PLUS_FLAG_ME; if(result != nullptr) flag |= STATUS_PLUS_FLAG_REF; status::dump(f); f.write((char *)&flag, 1); if(base != nullptr) base->dump(f); if(result != nullptr) result->dump(f); } void data_tree::status_plus::read(generic_file &f, unsigned char db_version) { unsigned char flag; detruit(); status::read(f, db_version); switch(db_version) { case 1: case 2: case 3: case 4: // nothing to be done, only a status class/struct // was used for these versions break; case 5: case 6: f.read((char *)&flag, 1); if((flag & STATUS_PLUS_FLAG_ME) != 0) base = create_crc_from_file(f, false); if((flag & STATUS_PLUS_FLAG_REF) != 0) result = create_crc_from_file(f, false); break; default: throw SRC_BUG; } } void data_tree::status_plus::copy_from(const status_plus & xref) { status *moi = this; const status *toi = &xref; *moi = *toi; if(xref.base != nullptr) { base = xref.base->clone(); if(base == nullptr) throw Ememory("data_tree::status_plus::copy_from"); } else base = nullptr; if(xref.result != nullptr) { result = xref.result->clone(); if(result == nullptr) throw Ememory("data_tree::status_plus::copy_from"); } else result = nullptr; } void data_tree::status_plus::move_from(status_plus && ref) noexcept { swap(base, ref.base); swap(result, ref.result); } void data_tree::status_plus::detruit() { if(base != nullptr) { delete base; base = nullptr; } if(result != nullptr) { delete result; result = nullptr; } } data_tree::data_tree(const string & name) { filename = name; } data_tree::data_tree(generic_file & f, unsigned char db_version) { archive_num k; status sta; status_plus sta_plus; // signature has already been read tools_read_string(f, filename); infinint tmp = infinint(f); // number of entry in last_mod map while(!tmp.is_zero()) { k.read_from_file(f); switch(db_version) { case 1: sta_plus.date = infinint(f); sta_plus.present = db_etat::et_saved; // me and ref fields are already set to nullptr last_mod[k] = sta_plus; break; case 2: case 3: case 4: case 5: case 6: sta_plus.read(f, db_version); last_mod[k] = sta_plus; break; default: // unsupported database version throw SRC_BUG; // this statement should have been prevented earlier in the code to avoid destroying or loosing data from the database } --tmp; } tmp = infinint(f); // number of entry in last_change map while(!tmp.is_zero()) { k.read_from_file(f); switch(db_version) { case 1: sta.date = infinint(f); sta.present = db_etat::et_saved; last_change[k] = sta; break; case 2: case 3: case 4: case 5: case 6: sta.read(f, db_version); last_change[k] = sta; break; default: throw SRC_BUG; } --tmp; } } void data_tree::dump(generic_file & f) const { char tmp = obj_signature(); infinint sz; map::const_iterator itp = last_mod.begin(); f.write(&tmp, 1); tools_write_string(f, filename); // last mod table dump sz = last_mod.size(); sz.dump(f); while(itp != last_mod.end()) { itp->first.write_to_file(f); // key itp->second.dump(f); // value ++itp; } // last change table dump sz = last_change.size(); sz.dump(f); map::const_iterator it = last_change.begin(); while(it != last_change.end()) { it->first.write_to_file(f); // key it->second.dump(f); // value ++it; } } db_lookup data_tree::get_data(set & archive, const datetime & date, bool even_when_removed) const { // we will pass each element of the last_mod map, following the order used by std::map // which is based on the order of the first element, here archive_num, thus "it" will // be pointing at each entry, from the smallest to the highest archive number datetime max_seen_date = datetime(0); archive_num last_archive_seen = 0; //< last archive number (in the order of the database) in which a valid entry has been found (any state) candidates candy(even_when_removed); //< Au pays de Candy, comme dans tous les pays ... map::const_iterator it = last_mod.begin(); while(it != last_mod.end()) { // this test is necessary to take care of problem of order in the database if(it->second.date >= max_seen_date // ">=" (not ">") because there should never be twice the same value // and we must be able to see a value of 0 (initially max = 0) which is valid. && (date.is_null() || it->second.date <= date)) { max_seen_date = it->second.date; last_archive_seen = it->first; candy.add(it->first, it->second.present); } ++it; } candy.set_the_set(archive); return candy.get_status(); } db_lookup data_tree::get_EA(archive_num & archive, const datetime & date, bool even_when_removed) const { map::const_iterator it = last_change.begin(); datetime max_seen_date = datetime(0), max_real_date = datetime(0); bool presence_seen = false, presence_real = false; archive_num last_archive_seen = 0; archive_num last_archive_even_when_removed = 0; db_lookup ret; archive = 0; // 0 is never assigned to an archive number while(it != last_change.end()) { if(it->second.date >= max_seen_date // > and = because there should never be twice the same value // and we must be able to see a value of 0 (initially max = 0) which is valid. && (date.is_null() || it->second.date <= date)) { max_seen_date = it->second.date; last_archive_seen = it->first; switch(it->second.present) { case db_etat::et_saved: case db_etat::et_present: presence_seen = true; break; case db_etat::et_removed: case db_etat::et_absent: presence_seen = false; break; default: throw SRC_BUG; } } if(it->second.date >= max_real_date // > and = because there should never be twice the same value // and we must be able to see a value of 0 (initially max = 0) which is valid. && (date.is_null() || it->second.date <= date)) { if(it->second.present != db_etat::et_present) { max_real_date = it->second.date; archive = it->first; switch(it->second.present) { case db_etat::et_saved: presence_real = true; last_archive_even_when_removed = archive; break; case db_etat::et_removed: case db_etat::et_absent: presence_real = false; break; case db_etat::et_present: throw SRC_BUG; default: throw SRC_BUG; } } } ++it; } if(even_when_removed && last_archive_even_when_removed > 0) { archive = last_archive_even_when_removed; presence_seen = presence_real = true; } if(archive == 0) if(last_archive_seen != 0) // last acceptable entry is a file present but not saved, but no full backup is present in a previous archive ret = db_lookup::not_restorable; else ret = db_lookup::not_found; else if(last_archive_seen != 0) if(presence_seen && !presence_real) // last acceptable entry is a file present but not saved, but no full backup is present in a previous archive ret = db_lookup::not_restorable; else if(presence_seen != presence_real) throw SRC_BUG; else // archive > 0 && presence_seen == present_real if(presence_real) ret = db_lookup::found_present; else ret = db_lookup::found_removed; else throw SRC_BUG; return ret; } bool data_tree::read_data(archive_num num, datetime & val, db_etat & present) const { map::const_iterator it = last_mod.find(num); if(it != last_mod.end()) { val = it->second.date; present = it->second.present; return true; } else return false; } bool data_tree::read_EA(archive_num num, datetime & val, db_etat & present) const { map::const_iterator it = last_change.find(num); if(it != last_change.end()) { val = it->second.date; present = it->second.present; return true; } else return false; } void data_tree::finalize(const archive_num & archive, const datetime & deleted_date, const archive_num & ignore_archives_greater_or_equal) { map::iterator itp = last_mod.begin(); bool presence_max = false; archive_num num_max = 0; datetime last_mtime = datetime(0); // used as deleted_date for EA if last mtime is found bool found_in_archive = false; // checking the last_mod map while(itp != last_mod.end() && !found_in_archive) { if(itp->first == archive && itp->second.present != db_etat::et_absent) found_in_archive = true; else if(itp->first > num_max && (itp->first < ignore_archives_greater_or_equal || ignore_archives_greater_or_equal == 0)) { num_max = itp->first; switch(itp->second.present) { case db_etat::et_saved: case db_etat::et_present: case db_etat::et_patch: case db_etat::et_patch_unusable: case db_etat::et_inode: presence_max = true; last_mtime = itp->second.date; // used as deleted_data for EA break; case db_etat::et_removed: case db_etat::et_absent: presence_max = false; last_mtime = itp->second.date; // keeping this date as it is // not possible to know when the EA have been removed // since it does not change mtime of file nor of its parent // directory (this is an heuristic). break; default: throw SRC_BUG; } } ++itp; } if(!found_in_archive) // not entry found for asked archive (or recorded as db_etat::et_absent) { if(presence_max) { // the most recent entry found stated that this file // existed at the time "last_mtime" // and this entry is absent from the archive under addition // thus we must record that it has been removed in the // archive we currently add. if(deleted_date > last_mtime) set_data(archive, deleted_date, db_etat::et_absent); // add an entry telling that this file does no more exist in the current archive else set_data(archive, last_mtime, db_etat::et_absent); // add an entry telling thatthis file does no more exists, using the last known date // as deleted data. This situation may appear when one makes a first backup // then a second one but excluding from the backup that particular file. This file // may reappear later with if is backup included in the backup possibily with the same // date. In 2.4.x we added 1 second to the last known date to create the deleted date // which lead out of order warning to show for the database. Since 2.5.x date resolution // may be one microsecond (no more 1 second) thus we now keep the last known date as // delete date } else // the entry has been seen previously but as removed in the latest state, // if we already have an et_absent entry (which is possible during a reordering archive operation within a database), we can (and must) suppress it. { itp = last_mod.find(archive); if(itp != last_mod.end()) // we have an entry associated to this archive { switch(itp->second.present) { case db_etat::et_saved: case db_etat::et_present: case db_etat::et_patch: case db_etat::et_patch_unusable: case db_etat::et_inode: throw SRC_BUG; // entry has not been found in the current archive case db_etat::et_removed: break; // we must keep it, it was in the original archive case db_etat::et_absent: last_mod.erase(itp); // this entry had been added from previous neighbor archive, we must remove it now, it was not part of the original archive break; default: throw SRC_BUG; } } // else already absent from the base for this archive, cool. } } // else, entry found for the current archive ////////////////////// // now checking the last_change map // map::iterator it = last_change.begin(); presence_max = false; num_max = 0; found_in_archive = false; while(it != last_change.end() && !found_in_archive) { if(it->first == archive && it->second.present != db_etat::et_absent) found_in_archive = true; else if(it->first > num_max && (it->first < ignore_archives_greater_or_equal || ignore_archives_greater_or_equal == 0)) { num_max = it->first; switch(it->second.present) { case db_etat::et_saved: case db_etat::et_present: presence_max = true; break; case db_etat::et_removed: case db_etat::et_absent: presence_max = false; break; case db_etat::et_patch: throw SRC_BUG; case db_etat::et_patch_unusable: throw SRC_BUG; default: throw SRC_BUG; } } ++it; } if(!found_in_archive) // not entry found for asked archive { if(num_max != 0) // num_max may be equal to zero if this entry never had any EA in any recorded archive if(presence_max) set_EA(archive, (last_mtime < deleted_date ? deleted_date : last_mtime), db_etat::et_absent); // add an entry telling that EA for this file do no more exist in the current archive // else last entry found stated the EA was removed, nothing more to do } // else, entry found for the current archive } bool data_tree::remove_all_from(const archive_num & archive_to_remove, const archive_num & last_archive) { map::iterator itp = last_mod.begin(); // if this file was stored as "removed" in the archive we tend to remove from the database // this "removed" information is propagated to the next archive if both: // - the next archive exists and has no information recorded about this file // - this entry does not only exist in the archive about to be removed if(archive_to_remove < last_archive) { datetime del_date; db_etat status; if(last_mod.size() > 1 && read_data(archive_to_remove, del_date, status)) if(status == db_etat::et_removed) { datetime tmp; if(!read_data(archive_to_remove + 1, tmp, status)) set_data(archive_to_remove + 1, del_date, db_etat::et_removed); } if(last_change.size() > 1 && read_EA(archive_to_remove, del_date, status)) if(status == db_etat::et_removed) { datetime tmp; if(!read_EA(archive_to_remove + 1, tmp, status)) set_EA(archive_to_remove + 1, del_date, db_etat::et_removed); } } while(itp != last_mod.end()) { if(itp->first == archive_to_remove) { last_mod.erase(itp); break; // stops the while loop, as there is at most one element with that key } else ++itp; } map::iterator it = last_change.begin(); while(it != last_change.end()) { if(it->first == archive_to_remove) { last_change.erase(it); break; // stops the while loop, as there is at most one element with that key } else ++it; } (void)check_delta_validity(); return last_mod.empty() && last_change.empty(); } void data_tree::listing(database_listing_get_version_callback callback, void *tag) const { map::const_iterator it = last_mod.begin(); map::const_iterator ut = last_change.begin(); while(it != last_mod.end() || ut != last_change.end()) { if(it != last_mod.end()) if(ut != last_change.end()) if(it->first == ut->first) { display_line(callback, tag, it->first, &(it->second.date), it->second.present, &(ut->second.date), ut->second.present); ++it; ++ut; } else // not in the same archive if(it->first < ut->first) // it only { display_line(callback, tag, it->first, &(it->second.date), it->second.present, nullptr, db_etat::et_removed); ++it; } else // ut only { display_line(callback, tag, ut->first, nullptr, db_etat::et_removed, &(ut->second.date), ut->second.present); ++ut; } else // ut at end of list thus it != last_mod.end() (see while condition) { display_line(callback, tag, it->first, &(it->second.date), it->second.present, nullptr, db_etat::et_removed); ++it; } else // it at end of list, this ut != last_change.end() (see while condition) { display_line(callback, tag, ut->first, nullptr, db_etat::et_removed, &(ut->second.date), ut->second.present); ++ut; } } } void data_tree::apply_permutation(archive_num src, archive_num dst) { map transfertp; map::iterator itp = last_mod.begin(); while(itp != last_mod.end()) { transfertp[data_tree_permutation(src, dst, itp->first)] = itp->second; ++itp; } last_mod = transfertp; transfertp.clear(); map transfert; map::iterator it = last_change.begin(); while(it != last_change.end()) { transfert[data_tree_permutation(src, dst, it->first)] = it->second; ++it; } last_change = transfert; (void)check_delta_validity(); } void data_tree::skip_out(archive_num num) { map resultantp; map::iterator itp = last_mod.begin(); infinint tmp; while(itp != last_mod.end()) { if(itp->first > num) resultantp[itp->first-1] = itp->second; else resultantp[itp->first] = itp->second; ++itp; } last_mod = resultantp; resultantp.clear(); map resultant; map::iterator it = last_change.begin(); while(it != last_change.end()) { if(it->first > num) resultant[it->first-1] = it->second; else resultant[it->first] = it->second; ++it; } last_change = resultant; } void data_tree::compute_most_recent_stats(deque & data, deque & ea, deque & total_data, deque & total_ea) const { archive_num most_recent = 0; datetime max = datetime(0); map::const_iterator itp = last_mod.begin(); while(itp != last_mod.end()) { if(itp->second.present == db_etat::et_saved) { if(itp->second.date >= max) { most_recent = itp->first; max = itp->second.date; } ++total_data[itp->first]; } ++itp; } if(most_recent > 0) ++data[most_recent]; most_recent = 0; max = datetime(0); map::const_iterator it = last_change.begin(); while(it != last_change.end()) { if(it->second.present == db_etat::et_saved) { if(it->second.date >= max) { most_recent = it->first; max = it->second.date; } ++total_ea[it->first]; } ++it; } if(most_recent > 0) ++ea[most_recent]; } bool data_tree::fix_corruption() { bool ret = true; map::iterator itp = last_mod.begin(); while(itp != last_mod.end() && ret) { if(itp->second.present != db_etat::et_removed && itp->second.present != db_etat::et_absent) ret = false; ++itp; } map::iterator it = last_change.begin(); while(it != last_change.end() && ret) { if(it->second.present != db_etat::et_removed && it->second.present != db_etat::et_absent) ret = false; ++it; } return ret; } // helper data structure for check_map_order method struct trecord { datetime date; bool set; trecord() { set = false; date = datetime(0); }; trecord(const trecord & ref) { date = ref.date; set = ref.set; }; trecord & operator = (const trecord & ref) { date = ref.date; set = ref.set; return *this; }; }; template bool data_tree::check_map_order(user_interaction & dialog, const map the_map, const path & current_path, const string & field_nature, bool & initial_warn) const { // some variable to work with around the_map U_I dates_size = the_map.size()+1; vector dates = vector(dates_size); typename map::const_iterator it = the_map.begin(); vector::iterator rec_it; datetime last_date = datetime(0); // now the algorithm // extracting dates from map into an ordered vector while(it != the_map.end()) { trecord rec; rec.date = it->second.date; rec.set = true; while(dates_size <= it->first) { dates.push_back(trecord()); ++dates_size; } dates[it->first] = rec; ++it; } // checking whether dates are sorted crescendo following the archive number rec_it = dates.begin(); while(rec_it != dates.end()) { if(rec_it->set) { if(rec_it->date >= last_date) last_date = rec_it->date; else // order is not respected { string tmp = current_path.display() == "." ? get_name() : (current_path.append(get_name())).display(); dialog.printf(gettext("Dates of file's %S are not increasing when database's archive number grows. Concerned file is: %S"), &field_nature, &tmp); if(initial_warn) { dialog.message(gettext("Dates are not increasing for all files when database's archive number grows, working with this database may lead to improper file's restored version. Please reorder the archive within the database in the way that the older is the first archive and so on up to the most recent archive being the last of the database")); try { dialog.pause(gettext("Do you want to ignore the same type of error for other files?")); return false; } catch(Euser_abort & e) { initial_warn = false; } } break; // aborting the while loop } } ++rec_it; } return true; } bool data_tree::check_delta_validity() { bool ret = true; const crc *prev = nullptr; for(map::iterator it = last_mod.begin(); it != last_mod.end(); ++it) { switch(it->second.present) { case db_etat::et_saved: prev = it->second.result; break; case db_etat::et_patch: case db_etat::et_patch_unusable: if(it->second.base == nullptr) throw SRC_BUG; if(prev != nullptr && *prev == *(it->second.base)) it->second.present = db_etat::et_patch; else { it->second.present = db_etat::et_patch_unusable; ret = false; } prev = it->second.result; break; case db_etat::et_present: case db_etat::et_inode: // we do not modify prev break; case db_etat::et_removed: case db_etat::et_absent: prev = nullptr; break; default: throw SRC_BUG; } } return ret; } archive_num data_tree::data_tree_permutation(archive_num src, archive_num dst, archive_num x) { if(src < dst) if(x < src || x > dst) return x; else if(x == src) return dst; else return x-1; else if(src == dst) return x; else // src > dst if(x > src || x < dst) return x; else if(x == src) return dst; else return x+1; } void data_tree::display_line(database_listing_get_version_callback callback, void *tag, archive_num num, const datetime *data, db_etat data_presence, const datetime *ea, db_etat ea_presence) { bool has_data_date = true; bool has_ea_date = true; if(data == nullptr) has_data_date = false; if(ea == nullptr) has_ea_date = false; if(callback == nullptr) throw Erange("data_tree::display_line", "nullptr given as callback function"); try { callback(tag, num, data_presence, has_data_date, has_data_date ? *data : datetime(0), ea_presence, has_ea_date, has_ea_date ? *ea : datetime(0)); } catch(...) { throw Elibcall("data_tree::display_line", "provided callback function should not throw any exception"); } } } // end of namespace dar-2.7.15/src/libdar/sar_tools.cpp0000644000175000017500000001137214636066467014060 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_ERRNO_H #include #endif } // end extern "C" #include "sar_tools.hpp" #include "erreurs.hpp" #include "user_interaction.hpp" #include "sar.hpp" #include "tools.hpp" #include "tuyau.hpp" #include "cygwin_adapt.hpp" #include "deci.hpp" using namespace std; namespace libdar { string sar_tools_make_filename(const string & base_name, const infinint & num, const infinint & min_digits, const string & ext) { deci conv = num; string digits = conv.human(); return base_name + '.' + sar_tools_make_padded_number(digits, min_digits) + '.' + ext; } bool sar_tools_extract_num(const string & filename, const string & base_name, const infinint & min_digits, const string & ext, infinint & ret) { try { U_I min_size = base_name.size() + ext.size() + 2; // 2 for two dot characters if(filename.size() <= min_size) return false; // filename is too short if(infinint(filename.size() - min_size) < min_digits && !min_digits.is_zero()) return false; // not enough room for all digits if(filename.find(base_name) != 0) // checking that base_name is present at the beginning return false; if(filename.rfind(ext) != filename.size() - ext.size()) // checking that extension is at the end return false; deci conv = string(filename.begin()+base_name.size()+1, filename.begin() + (filename.size() - ext.size()-1)); ret = conv.computer(); return true; } catch(Ethread_cancel & e) { throw; } catch(Egeneric &e) { return false; } } bool sar_tools_get_higher_number_in_dir(user_interaction & ui, entrepot & entr, const string & base_name, const infinint & min_digits, const string & ext, infinint & ret) { infinint cur; bool somme = false; string entry; try { entr.read_dir_reset(); } catch(Erange & e) { e.prepend_message(gettext("Error met while looking for slices: ")); ui.message(e.get_message()); return false; } ret = 0; somme = false; while(entr.read_dir_next(entry)) if(sar_tools_extract_num(entry, base_name, min_digits, ext, cur)) { if(cur > ret) ret = cur; somme = true; } return somme; } void sar_tools_remove_higher_slices_than(entrepot & entr, const string & base_name, const infinint & min_digits, const string & ext, const infinint & higher_slice_num_to_keep, user_interaction & ui) { infinint cur; string entry; try { entr.read_dir_reset(); } catch(Erange & e) { return; } while(entr.read_dir_next(entry)) if(sar_tools_extract_num(entry, base_name, min_digits, ext, cur)) { if(cur > higher_slice_num_to_keep) entr.unlink(entry); } } string sar_tools_make_padded_number(const string & num, const infinint & min_digits) { string ret = num; while(infinint(ret.size()) < min_digits) ret = string("0") + ret; return ret; } } // end of namespace dar-2.7.15/src/libdar/lzo_module.cpp0000644000175000017500000001251114636066467014220 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_LZO_LZO1X_H #include #endif } #include "lzo_module.hpp" #include "int_tools.hpp" #include "tools.hpp" using namespace std; namespace libdar { U_I lzo_module::get_max_compressing_size() const { #if LIBLZO2_AVAILABLE return 256*1024l*1024l; // from MAX_BLOCK_SIZE in p.lzo.c (lzop source code) #else throw Ecompilation(gettext("lzo compression")); #endif } U_I lzo_module::get_min_size_to_compress(U_I clear_size) const { #if LIBLZO2_AVAILABLE return clear_size + clear_size / 16 + 64 + 3; // from MAX_COMPRESSED_SIZE(x) macro in p_lzo.c from lzop source code #else throw Ecompilation(gettext("lzo compression")); #endif } U_I lzo_module::compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const { #if LIBLZO2_AVAILABLE S_I status; lzo_uint zip_buf_size_lzo = zip_buf_size; switch(lzo_algo) { case compression::lzo: status = lzo1x_999_compress_level((lzo_bytep)normal, normal_size, (lzo_bytep)zip_buf, &zip_buf_size_lzo, wrkmem_compr.get(), nullptr, 0, nullptr, level); break; case compression::lzo1x_1_15: status = lzo1x_1_15_compress((lzo_bytep)normal, normal_size, (lzo_bytep)zip_buf, &zip_buf_size_lzo, wrkmem_compr.get()); break; case compression::lzo1x_1: status = lzo1x_1_compress((lzo_bytep)normal, normal_size, (lzo_bytep)zip_buf, &zip_buf_size_lzo, wrkmem_compr.get()); break; default: throw SRC_BUG; } zip_buf_size = zip_buf_size_lzo; if((lzo_uint)(zip_buf_size) != zip_buf_size_lzo) throw SRC_BUG; switch(status) { case LZO_E_OK: break; // all is fine case LZO_E_ERROR: throw Erange("lzo_module::compress_data", "invalid compresion level or argument provided"); default: throw Erange("lzo_module::compress_data", tools_printf(gettext("Probable bug in liblzo2: lzo1x_*_compress returned unexpected/undocumented code %d"), status)); } return zip_buf_size; #else throw Ecompilation(gettext("lzo compression")); #endif } U_I lzo_module::uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const { #if LIBLZO2_AVAILABLE S_I status; lzo_uint normal_size_lzo = normal_size; status = lzo1x_decompress_safe((lzo_bytep)zip_buf, zip_buf_size, (lzo_bytep)normal, & normal_size_lzo, wrkmem_decompr.get()); normal_size = normal_size_lzo; if((lzo_uint)(normal_size) != normal_size_lzo) throw SRC_BUG; // integer overflow occured switch(status) { case LZO_E_OK: break; // all is fine case LZO_E_INPUT_NOT_CONSUMED: case LZO_E_INPUT_OVERRUN: case LZO_E_LOOKBEHIND_OVERRUN: throw Edata(gettext("corrupted compressed data met")); default: throw Edata(gettext("Corrupted compressed data met")); } return normal_size; #else throw Ecompilation(gettext("lzo compression")); #endif } unique_ptr lzo_module::clone() const { #if LIBLZO2_AVAILABLE try { return std::make_unique(*this); } catch(bad_alloc &) { throw Ememory("lzo_module::clone"); } #else throw Ecompilation(gettext("lzo compression")); #endif } void lzo_module::init(compression algo, U_I compression_level) { #if LIBLZO2_AVAILABLE if(compression_level > 9 || compression_level < 1) throw Erange("lzo_module::lzo_module", tools_printf(gettext("out of range LZO compression level: %d"), compression_level)); level = compression_level; if(algo != compression::lzo && algo != compression::lzo1x_1_15 && algo != compression::lzo1x_1) throw Erange("lzo_module::lzo_module", "invalid lzo compression algoritm provided"); lzo_algo = algo; try { if(LZO1X_MEM_DECOMPRESS > 0) wrkmem_decompr = make_unique(LZO1X_MEM_DECOMPRESS); else wrkmem_decompr.reset(); // points to nullptr switch(lzo_algo) { case compression::lzo: wrkmem_compr = make_unique(LZO1X_999_MEM_COMPRESS); break; case compression::lzo1x_1_15: wrkmem_compr = make_unique(LZO1X_1_15_MEM_COMPRESS); break; case compression::lzo1x_1: wrkmem_compr = make_unique(LZO1X_1_MEM_COMPRESS); break; default: throw SRC_BUG; } } catch(bad_alloc &) { throw Ememory("lzo_module::lzo_module"); } #else throw Ecompilation(gettext("lzo compression")); #endif } } // end of namespace dar-2.7.15/src/libdar/archive_options.hpp0000644000175000017500000030201714636067146015246 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive_options.hpp /// \brief this file contains a set of classes used to transmit options to archive operation /// \ingroup API #ifndef ARCHIVE_OPTIONS_HPP #define ARCHIVE_OPTIONS_HPP #include "../my_config.h" #include "crypto.hpp" #include "integers.hpp" #include "mask.hpp" #include "mask_list.hpp" #include "crit_action.hpp" #include "secu_string.hpp" #include "entrepot.hpp" #include "fsa_family.hpp" #include "compile_time_features.hpp" #include "archive_aux.hpp" #include "compression.hpp" #include "delta_sig_block_size.hpp" #include "filesystem_ids.hpp" #include #include #include #include namespace libdar { class archive; // needed to be able to use pointer on archive object. ///////////////////////////////////////////////////////// ////////////// OPTIONS FOR OPENNING AN ARCHIVE ////////// ///////////////////////////////////////////////////////// /// \addtogroup API /// @{ /// class holding optional parameters used to read an existing archive class archive_options_read { public: /// build an object and set options to their default values archive_options_read(); /// the copy constructor, assignment operator and destructor archive_options_read(const archive_options_read & ref) : x_ref_chem(ref.x_ref_chem) { copy_from(ref); }; archive_options_read(archive_options_read && ref) noexcept; archive_options_read & operator = (const archive_options_read & ref) { copy_from(ref); return *this; }; archive_options_read & operator = (archive_options_read && ref) noexcept { move_from(std::move(ref)); return *this; }; ~archive_options_read() = default; ///////////////////////////////////////////////////////////////////// // set back to default (this is the state just after the object is constructed // this method is to be used to reuse a given object /// reset all the options to their default values void clear(); ///////////////////////////////////////////////////////////////////// // setting methods /// defines the the crypto cypher to use to read the archive (default is crypto_none) /// \note since release 2.5.0 you may and should provide crypto_none in any case (the default value) /// for libdar uses the algorithm stored in the archive. However you way override this /// (in case of corruption for example) by explicitely specifying a crypto algorithm /// in that case the value of the crypto algorithm stored in the archive is ignored /// and the algorithm used to decipher is the one specified here. void set_crypto_algo(crypto_algo val) { x_crypto = val; }; /// defines the password or passphrase to decrypt (unused if encryption is not set) void set_crypto_pass(const secu_string & pass) { x_pass = pass; }; /// the encryption block size to use to decrypt void set_crypto_size(U_32 crypto_size) { x_crypto_size = crypto_size; }; /// set the encryption block size to the default value void set_default_crypto_size(); /// set the name of the input pipe to read data from (when basename is set to "-") /// if input_pipe is set to "" (empty string) the information from dar_slave are expected /// in standard input else the given string void set_input_pipe(const std::string & input_pipe) { x_input_pipe = input_pipe; }; /// set the name of the output pipe to send orders to (when basenale is set to "-") /// if output_pipe is set to "" the orders sent to dar_slave will exit by the standard output else the given string /// must be the path to a named pipe which will relay the orders to dar_slave void set_output_pipe(const std::string & output_pipe) { x_output_pipe = output_pipe; }; /// set the command to execute before reading each slice (empty string for no script) /// several macros are available: /// - %%n : the slice number to be read /// - %%N : the slice number with padded zeros according to slice_min_digits given option /// - %%b : the archive basename /// - %%p : the slices path /// - %%e : the archive extension (usually "dar") /// - %%c : the archive context ("init", "operation" or "last_slice") see dar(1) man page for more details /// - %%% : substitued by %% /// . void set_execute(const std::string & execute) { x_execute = execute; }; /// defines whether the user needs detailed output of the operation void set_info_details(bool info_details) { x_info_details = info_details; }; /// defines whether any archive coherence error, system error or media error lead to the abortion of the operation /// lax mode is false by default. /// setting it to true, may allow more data to be restored, but may lead the user to get corrupted data /// the user will be warned and asked upon what to do if such case arrives. void set_lax(bool val) { x_lax = val; }; /// defines whether to try reading the archive sequentially (ala tar) or using the final catalogue /// the sequential reading must not has been disabled at creation time and the archive must be of minimum format "08" for the operation not to fail void set_sequential_read(bool val) { x_sequential_read = val; }; /// defines the minimum digit a slice must have concerning its number, zeros will be prepended as much as necessary to respect this void set_slice_min_digits(infinint val) { x_slice_min_digits = val; }; /// defines the protocol to use to retrieve slices void set_entrepot(const std::shared_ptr & entr) { if(!entr) throw Erange("archive_options_read::set_entrepot", "null entrepot pointer given in argument"); x_entrepot = entr; }; /// whether to warn (true) or ignore (false) signature failure (default is true) void set_ignore_signature_check_failure(bool val) { x_ignore_signature_check_failure = val; }; /// whether libdar is allowed to create several thread to work possibly faster on multicore CPU (need libthreadar to be effective) /// \deprecated this call is deprecated, see set_multi_threaded_*() more specific calls /// \note setting this to true is equivalent to calling set_mutli_threaded_crypto(2) void set_multi_threaded(bool val) { x_multi_threaded_crypto = 2; x_multi_threaded_compress = 2; }; /// how much thread libdar will use for cryptography (need libthreadar to be effective) void set_multi_threaded_crypto(U_I num) { x_multi_threaded_crypto = num; }; /// how much thread libdar will use for compression (need libthreadar too and compression_block_size > 0) void set_multi_threaded_compress(U_I num) { x_multi_threaded_compress = num; }; //////// what follows concerne the use of an external catalogue instead of the archive's internal one /// defines whether or not to use the catalogue from an extracted catalogue (instead of the one embedded in the archive) and which one to use void set_external_catalogue(const path & ref_chem, const std::string & ref_basename) { x_ref_chem = ref_chem, x_ref_basename = ref_basename; external_cat = true; }; /// clear any reference to an external catalogue void unset_external_catalogue(); /// defines the crypto algo for the reference catalogue void set_ref_crypto_algo(crypto_algo ref_crypto) { x_ref_crypto = ref_crypto; }; /// defines the pass for the reference catalogue void set_ref_crypto_pass(const secu_string & ref_pass) { x_ref_pass = ref_pass; }; /// defines the crypto size for the reference catalogue void set_ref_crypto_size(U_32 ref_crypto_size) { x_ref_crypto_size = ref_crypto_size; }; /// set the command to execute before reading each slice of the reference catalogue /// several macros are available: /// - %%n : the slice number to be read /// - %%N : the slice number with padded zeros according to slice_min_digits given option /// - %%b : the archive basename /// - %%p : the slices path /// - %%e : the archive extension (usually "dar") /// - %%c : the archive context ("init", "operation" or "last_slice") see dar(1) man page for more details /// - %%% : substitued by %% /// . void set_ref_execute(const std::string & ref_execute) { x_ref_execute = ref_execute; }; /// defines the minim digit for slice number of the archive of reference (where the external catalogue is read from) void set_ref_slice_min_digits(infinint val) { x_ref_slice_min_digits = val; }; /// defines the protocol to use to retrieve slices of the reference archive (where the external catalogue resides) void set_ref_entrepot(const std::shared_ptr & entr) { if(!entr) throw Erange("archive_options_read::set_ref_entrepot", "null entrepot pointer given in argument"); x_ref_entrepot = entr; }; /// whether we only read the archive header and exit void set_header_only(bool val) { x_header_only = val; }; ///////////////////////////////////////////////////////////////////// // getting methods (mainly used inside libdar, but kept public and part of the API in the case it is needed) crypto_algo get_crypto_algo() const { return x_crypto; }; const secu_string & get_crypto_pass() const { return x_pass; }; U_32 get_crypto_size() const { return x_crypto_size; }; const std::string & get_input_pipe() const { return x_input_pipe; }; const std::string & get_output_pipe() const { return x_output_pipe; }; const std::string & get_execute() const { return x_execute; }; bool get_info_details() const { return x_info_details; }; bool get_lax() const { return x_lax; }; bool get_sequential_read() const { return x_sequential_read; }; infinint get_slice_min_digits() const { return x_slice_min_digits; }; const std::shared_ptr & get_entrepot() const { return x_entrepot; }; bool get_ignore_signature_check_failure() const { return x_ignore_signature_check_failure; }; U_I get_multi_threaded_crypto() const { return x_multi_threaded_crypto; }; U_I get_multi_threaded_compress() const { return x_multi_threaded_compress; }; // All methods that follow concern the archive where to fetch the (isolated) catalogue from bool is_external_catalogue_set() const { return external_cat; }; const path & get_ref_path() const; const std::string & get_ref_basename() const; crypto_algo get_ref_crypto_algo() const { return x_ref_crypto; }; const secu_string & get_ref_crypto_pass() const { return x_ref_pass; }; U_32 get_ref_crypto_size() const { return x_ref_crypto_size; }; const std::string & get_ref_execute() const { return x_ref_execute; }; infinint get_ref_slice_min_digits() const { return x_ref_slice_min_digits; }; const std::shared_ptr & get_ref_entrepot() const { return x_ref_entrepot; }; bool get_header_only() const { return x_header_only; }; private: crypto_algo x_crypto; secu_string x_pass; U_32 x_crypto_size; std::string x_input_pipe; std::string x_output_pipe; std::string x_execute; bool x_info_details; bool x_lax; bool x_sequential_read; infinint x_slice_min_digits; std::shared_ptr x_entrepot; bool x_ignore_signature_check_failure; U_I x_multi_threaded_crypto; U_I x_multi_threaded_compress; // external catalogue relative fields bool external_cat; path x_ref_chem; std::string x_ref_basename; crypto_algo x_ref_crypto; secu_string x_ref_pass; U_32 x_ref_crypto_size; std::string x_ref_execute; infinint x_ref_slice_min_digits; std::shared_ptr x_ref_entrepot; bool x_header_only; void copy_from(const archive_options_read & ref); void move_from(archive_options_read && ref) noexcept; }; ///////////////////////////////////////////////////////// ///////// OPTIONS FOR CREATING AN ARCHIVE /////////////// ///////////////////////////////////////////////////////// /// class holding optional parameters used to create an archive class archive_options_create { public: // default constructors and destructor. archive_options_create(); archive_options_create(const archive_options_create & ref); archive_options_create(archive_options_create && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; archive_options_create & operator = (const archive_options_create & ref) { destroy(); copy_from(ref); return *this; }; archive_options_create & operator = (archive_options_create && ref) noexcept { move_from(std::move(ref)); return *this; }; ~archive_options_create() { destroy(); }; ///////////////////////////////////////////////////////////////////// // set back to default (this is the state just after the object is constructed // this method is to be used to reuse a given object /// reset all the options to their default values void clear(); ///////////////////////////////////////////////////////////////////// // setting methods /// set the archive to take as reference (nullptr for a full backup) void set_reference(std::shared_ptr ref_arch) { x_ref_arch = ref_arch; }; /// defines the filenames to only save (except directory) as those that match the given mask void set_selection(const mask & selection); /// defines the directories and files to consider /// \note WARNING: this mask will be applied to the absolute path of files being proceeded. /// We speak here about the root of the filesystem under which the fs_root directory contains /// the files to backup. This is independent from the fs_root argument of class archive /// constructor which objective is to reduce the perimeter of the backup. The subtree filters /// do not compare only to the path inside the fs_root directory but to the full path, /// including the fs_root directory. In other words, if the subtree mask do not accept /// anything under fs_root path, the resulting backup will be empty. void set_subtree(const mask & subtree); /// defines whether overwritting is allowed or not void set_allow_over(bool allow_over) { x_allow_over = allow_over; }; /// defines whether a warning shall be issued before overwriting void set_warn_over(bool warn_over) { x_warn_over = warn_over; }; /// defines whether the user needs detailed output of the operation /// \note in API 5.5.x and before this switch drove the displaying /// of processing messages and treated files. now it only drives the /// display of processing messages, use set_display_treated to define /// whether files under treatement should be display or not void set_info_details(bool info_details) { x_info_details = info_details; }; /// defines whether to show treated files /// \param[in] display_treated true to display processed inodes /// \param[in] only_dir only display the current directory under process, not its individual files void set_display_treated(bool display_treated, bool only_dir) { x_display_treated = display_treated; x_display_treated_only_dir = only_dir; }; /// whether to display files that have been excluded by filters void set_display_skipped(bool display_skipped) { x_display_skipped = display_skipped; }; /// whether to display a summary for each completed directory with total saved data and compression ratio void set_display_finished(bool display_finished) { x_display_finished = display_finished; }; /// set a pause beteween slices. Set to zero does not pause at all, set to 1 makes libdar pauses each slice, set to 2 makes libdar pause each 2 slices and so on. void set_pause(const infinint & pause) { x_pause = pause; }; /// defines whether we need to store ignored directories as empty void set_empty_dir(bool empty_dir) { x_empty_dir = empty_dir; }; /// set the compression algorithm to be used void set_compression(compression compr_algo) { x_compr_algo = compr_algo; }; /// set the compression level (from 1 to 9) void set_compression_level(U_I compression_level) { x_compression_level = compression_level; }; /// set the compression block size /// \param[in] compression_block_size if set to zero (which is the default value) /// compression is performed without block in one single stream per file. /// This is the historical way used by libdar, it gives the best result /// and the lowest compute overhead, though it cannot be parallelized. /// At the opposite using compresion per block reduce the compression ratio /// but allows the block to be compressed/decompressed in parallel and thus /// leverage multi-core systems. When the block size increase you tend to the /// same compression ratio as compression ration without block void set_compression_block_size(U_I compression_block_size) { x_compression_block_size = compression_block_size; }; /// define the archive slicing /// \param[in] file_size set the slice size in byte (0 for a single slice whatever its size is) /// \param[in] first_file_size set the first file size /// \note if not specified or set to zero, first_file_size is considered equal to file_size void set_slicing(const infinint & file_size, const infinint & first_file_size = 0) { x_file_size = file_size; if(first_file_size.is_zero()) x_first_file_size = file_size; else x_first_file_size = first_file_size; }; /// defines which Extended Attributes to save void set_ea_mask(const mask & ea_mask); /// set the command to execute after each slice creation /// several macros are available: /// - %%n : the number of the just created slice /// - %%N : the slice number with padded zeros according to slice_min_digits given option /// - %%b : the archive basename /// - %%p : the slices path /// - %%e : the archive extension (usually "dar") /// - %%c : the archive context ("init", "operation" or "last_slice") see dar(1) man page for more details /// - %%% : substitued by %% /// . void set_execute(const std::string & execute) { x_execute = execute; }; /// set the cypher to use void set_crypto_algo(crypto_algo crypto) { x_crypto = crypto; }; /// set the pass the password / passphrase to use. Giving an empty string makes the password asked /// interactively through the dialog argument if encryption has been set. void set_crypto_pass(const secu_string & pass) { x_pass = pass; }; /// set the size of the encryption by block to use void set_crypto_size(U_32 crypto_size) { x_crypto_size = crypto_size; }; /// set the list of recipients that will be able to read the archive /// \note this is based on GnuPG keyring and assumes the user running libdar has its keyring /// containing for each recipient a valid public key. If a list of recipient is given the crypto_pass /// (see above) is not used, but the crypto_algo stays used to encrypt the archive using a randomly generated key /// which is encrypted using the public keys of the recipients and dropped that way encrypted inside the archive. /// \note if crypto_algo is not set while a list of recipient is given, the crypto algo will default to blowfish /// \note since release 2.7.0 if a given std::string in the list contains an '@' the string is assumed to be an /// email and search is done in the keyring for that field type, else it is assumed to be a keyid. void set_gnupg_recipients(const std::vector & gnupg_recipients) { x_gnupg_recipients = gnupg_recipients; }; /// the private keys matching the email or the keyid of the provided list are used to sign the archive random key /// \note since release 2.7.0 if a given std::string in the list contains an '@' the string is assumed to be an /// email and search is done in the keyring for that field type, else it is assumed to be a keyid. void set_gnupg_signatories(const std::vector & gnupg_signatories) { x_gnupg_signatories = gnupg_signatories; }; /// defines files to compress void set_compr_mask(const mask & compr_mask); /// defines file size under which to never compress void set_min_compr_size(const infinint & min_compr_size) { x_min_compr_size = min_compr_size; }; /// defines whether to ignore files with the nodump flag set void set_nodump(bool nodump) { x_nodump = nodump; }; /// defines whether to ignore files having a given EA /// \note if ea_name is set to "" the default ea_name "user.libdar_no_backup" is used. void set_exclude_by_ea(const std::string & ea_name) { exclude_by_ea = (ea_name == "" ? "user.libdar_no_backup" : ea_name); }; /// set the fields to consider when comparing inodes with reference archive (see comparison_fields enumeration in catalogue.hpp) void set_what_to_check(comparison_fields what_to_check) { x_what_to_check = what_to_check; }; /// ignore differences of at most this integer number of hours while looking for changes in dates void set_hourshift(const infinint & hourshift) { x_hourshift = hourshift; }; /// whether to make a dry-run operation void set_empty(bool empty) { x_empty = empty; }; /// whether to alter atime or ctime in the filesystem when reading files to save /// \param[in] alter_atime whether to change atime (true) or ctime (false) /// \note this parameter is used only when furtive_read_mode() is not activated void set_alter_atime(bool alter_atime) { if(x_furtive_read) x_old_alter_atime = alter_atime; else x_alter_atime = alter_atime; }; /// whether to use furtive read mode (if activated, alter_atime() has no meaning/use) void set_furtive_read_mode(bool furtive_read); /// whether to limit the backup to files located on the same filesystem as the directory taken as root of the backup /// \note if using this method, all value set using set_same_fs_include and set_same_fs_exclude so far are /// forgotten and ignored. void set_same_fs(bool same_fs) { x_same_fs = same_fs; x_same_fs_include.clear(); x_same_fs_exclude.clear(); }; /// files on the filesystem pointed to by the given path will be considered for the backup operation if not excluded by set_same_fs_exclude() /// \note if using this method any previous call to the legacy set_same_fs(bool) invocation is ignored void set_same_fs_include(const std::string & included_path_to_fs) { x_same_fs_include.push_back(included_path_to_fs); }; /// files on the filesystem pointed to by the given path will not be considered for backup operation /// \note if using this method any previous call to the legacy set_same_fs(bool) invocation is ignored void set_same_fs_exclude(const std::string & excluded_path_to_fs) { x_same_fs_exclude.push_back(excluded_path_to_fs); }; /// whether to make an emtpy archive only referencing the current state of files in the filesystem void set_snapshot(bool snapshot) { x_snapshot = snapshot; }; /// whether to consider the Cache Directory Tagging Standard void set_cache_directory_tagging(bool cache_directory_tagging) { x_cache_directory_tagging = cache_directory_tagging; }; /// whether to ignore any archive of reference and only save file which modification is more recent that the given "fixed_date". To not use this feature set fixed_date value of zero (which is the value by default) void set_fixed_date(const infinint & fixed_date) { x_fixed_date = fixed_date; }; /// if not an empty string set the slice permission according to the octal value given. void set_slice_permission(const std::string & slice_permission) { x_slice_permission = slice_permission; }; /// if not an empty string set the user ownership of slices accordingly void set_slice_user_ownership(const std::string & slice_user_ownership) { x_slice_user_ownership = slice_user_ownership; }; /// if not an empty string set the group ownership of slices accordingly void set_slice_group_ownership(const std::string & slice_group_ownership) { x_slice_group_ownership = slice_group_ownership; }; /// how much time to retry saving a file if it changed while being read void set_retry_on_change(const infinint & count_max_per_file, const infinint & global_max_byte_overhead = 0) { x_repeat_count = count_max_per_file; x_repeat_byte = global_max_byte_overhead; }; /// whether to add escape sequence aka tape marks to allow sequential reading of the archive void set_sequential_marks(bool sequential) { x_sequential_marks = sequential; }; /// whether to try to detect sparse files void set_sparse_file_min_size(const infinint & size) { x_sparse_file_min_size = size; }; /// whether to check for ctime changes since with the archive of reference void set_security_check(bool check) { x_security_check = check; }; /// specify a user comment in the archive (always in clear text!) void set_user_comment(const std::string & comment) { x_user_comment = comment; }; /// specify whether to produce a hash file of the slice and which hash algo to use /// \note the libdar::hash_algo data type is defined in hash_fichier.hpp, valid values /// are for examle libdar::hash_none, libdar::hash_md5, libdar::hash_sha1, libdar::hash_sha512... void set_hash_algo(hash_algo hash); /// defines the minimum digit a slice must have concerning its number, zeros will be prepended as much as necessary to respect this void set_slice_min_digits(infinint val) { x_slice_min_digits = val; }; /// defines the backup hook for files void set_backup_hook(const std::string & execute, const mask & which_files); /// whether to ignore unknown inode types instead of issuing a warning void set_ignore_unknown_inode_type(bool val) { x_ignore_unknown = val; }; /// defines the protocol to use for slices void set_entrepot(const std::shared_ptr & entr) { if(!entr) throw Erange("archive_options_create::set_entrepot", "null entrepot pointer given in argument"); x_entrepot = entr; }; /// defines the FSA (Filesystem Specific Attribute) to only consider (by default all FSA activated at compilation time are considered) void set_fsa_scope(const fsa_scope & scope) { x_scope = scope; }; /// whether libdar is allowed to spawn several threads to possibily work faster on multicore CPU (requires libthreadar) /// \deprecated this call is deprecated, see set_multi_threaded_*() more specific calls /// \note setting this to true is equivalent to calling set_mutli_threaded_crypto(2) void set_multi_threaded(bool val) { x_multi_threaded_crypto = 2; x_multi_threaded_compress = 2; }; /// how much thread libdar will use for cryptography (need libthreadar to be effective) void set_multi_threaded_crypto(U_I num) { x_multi_threaded_crypto = num; }; /// how much thread libdar will use for compression (need libthreadar too and compression_block_size > 0) void set_multi_threaded_compress(U_I num) { x_multi_threaded_compress = num; }; /// whether binary delta has to be computed for differential/incremental backup /// \note this requires delta signature to be present in the archive of reference void set_delta_diff(bool val) { if(val && !compile_time::librsync()) throw Ecompilation("librsync"); x_delta_diff = val; }; /// whether signature to base binary delta on the future has to be calculated and stored beside saved files void set_delta_signature(bool val) { x_delta_signature = val; }; /// whether to derogate to defaut delta file consideration while calculation delta signatures void set_delta_mask(const mask & delta_mask); /// whether to never calculate delta signature for files which size is smaller or equal to the given argument /// \note by default a min size of 10 kiB is used void set_delta_sig_min_size(const infinint & val) { x_delta_sig_min_size = val; }; /// whether to automatically zeroing negative dates read from the filesystem (just warn, don't ask whether to pursue) void set_auto_zeroing_neg_dates(bool val) { x_auto_zeroing_neg_dates = val; }; /// provide a list of full path which if are symlinks will be considered as the inode they point to /// \note this is espetially intended for use with symlinks pointing to directories /// to have dar recursing in such pointed to directory instead of just recording that directory void set_ignored_as_symlink(const std::set & list) { x_ignored_as_symlink = list; }; /// defines when to resave a file's data which inode metadata changed void set_modified_data_detection(modified_data_detection val) { x_modified_data_detection = val; }; /// key derivation void set_iteration_count(const infinint & val) { x_iteration_count = val; }; /// hash algo used for key derivation void set_kdf_hash(hash_algo algo) { x_kdf_hash = algo; }; /// block size to use to build delta signatures void set_sig_block_len(delta_sig_block_size val) { val.check(); x_sig_block_len = val; }; ///////////////////////////////////////////////////////////////////// // getting methods std::shared_ptr get_reference() const { return x_ref_arch; }; const mask & get_selection() const { if(x_selection == nullptr) throw SRC_BUG; return *x_selection; }; const mask & get_subtree() const { if(x_subtree == nullptr) throw SRC_BUG; return *x_subtree; }; bool get_allow_over() const { return x_allow_over; }; bool get_warn_over() const { return x_warn_over; }; bool get_info_details() const { return x_info_details; }; bool get_display_treated() const { return x_display_treated; }; bool get_display_treated_only_dir() const { return x_display_treated_only_dir; }; bool get_display_skipped() const { return x_display_skipped; }; bool get_display_finished() const { return x_display_finished; }; const infinint & get_pause() const { return x_pause; }; bool get_empty_dir() const { return x_empty_dir; }; compression get_compression() const { return x_compr_algo; }; U_I get_compression_level() const { return x_compression_level; }; U_I get_compression_block_size() const { return x_compression_block_size; }; const infinint & get_slice_size() const { return x_file_size; }; const infinint & get_first_slice_size() const { return x_first_file_size; }; const mask & get_ea_mask() const { if(x_ea_mask == nullptr) throw SRC_BUG; return *x_ea_mask; }; const std::string & get_execute() const { return x_execute; }; crypto_algo get_crypto_algo() const { return x_crypto; }; const secu_string & get_crypto_pass() const { return x_pass; }; U_32 get_crypto_size() const { return x_crypto_size; }; const std::vector & get_gnupg_recipients() const { return x_gnupg_recipients; }; const std::vector & get_gnupg_signatories() const { return x_gnupg_signatories; }; const mask & get_compr_mask() const { if(x_compr_mask == nullptr) throw SRC_BUG; return *x_compr_mask; }; const infinint & get_min_compr_size() const { return x_min_compr_size; }; bool get_nodump() const { return x_nodump; }; const std::string & get_exclude_by_ea() const { return exclude_by_ea; }; comparison_fields get_comparison_fields() const { return x_what_to_check; }; const infinint & get_hourshift() const { return x_hourshift; }; bool get_empty() const { return x_empty; }; bool get_alter_atime() const { return x_alter_atime; }; bool get_furtive_read_mode() const { return x_furtive_read; }; bool get_same_fs() const { return x_same_fs; }; std::deque get_same_fs_include() const { return x_same_fs_include; }; std::deque get_same_fs_exclude() const { return x_same_fs_exclude; }; bool get_snapshot() const { return x_snapshot; }; bool get_cache_directory_tagging() const { return x_cache_directory_tagging; }; const infinint & get_fixed_date() const { return x_fixed_date; }; const std::string & get_slice_permission() const { return x_slice_permission; }; const std::string & get_slice_user_ownership() const { return x_slice_user_ownership; }; const std::string & get_slice_group_ownership() const { return x_slice_group_ownership; }; const infinint & get_repeat_count() const { return x_repeat_count; }; const infinint & get_repeat_byte() const { return x_repeat_byte; }; bool get_sequential_marks() const { return x_sequential_marks; }; infinint get_sparse_file_min_size() const { return x_sparse_file_min_size; }; bool get_security_check() const { return x_security_check; }; const std::string & get_user_comment() const { return x_user_comment; }; hash_algo get_hash_algo() const { return x_hash; }; infinint get_slice_min_digits() const { return x_slice_min_digits; }; const std::string & get_backup_hook_file_execute() const { return x_backup_hook_file_execute; }; const mask & get_backup_hook_file_mask() const { return *x_backup_hook_file_mask; }; bool get_ignore_unknown_inode_type() const { return x_ignore_unknown; }; const std::shared_ptr & get_entrepot() const { return x_entrepot; }; const fsa_scope & get_fsa_scope() const { return x_scope; }; U_I get_multi_threaded_crypto() const { return x_multi_threaded_crypto; }; U_I get_multi_threaded_compress() const { return x_multi_threaded_compress; }; bool get_delta_diff() const { return x_delta_diff; }; bool get_delta_signature() const { return x_delta_signature; }; const mask & get_delta_mask() const { return *x_delta_mask; } bool get_has_delta_mask_been_set() const { return has_delta_mask_been_set; }; const infinint & get_delta_sig_min_size() const { return x_delta_sig_min_size; }; bool get_auto_zeroing_neg_dates() const { return x_auto_zeroing_neg_dates; }; const std::set & get_ignored_as_symlink() const { return x_ignored_as_symlink; }; modified_data_detection get_modified_data_detection() const { return x_modified_data_detection; }; const infinint & get_iteration_count() const { return x_iteration_count; }; hash_algo get_kdf_hash() const { return x_kdf_hash; }; delta_sig_block_size get_sig_block_len() const { return x_sig_block_len; }; private: std::shared_ptr x_ref_arch; ///< just contains the address of an existing object, no local copy of object is done here mask * x_selection; ///< points to a local copy of mask (must be allocated / releases by the archive_option_create object) mask * x_subtree; ///< points to a local copy of mask (must be allocated / releases by the archive_option_create objects) bool x_allow_over; bool x_warn_over; bool x_info_details; bool x_display_treated; bool x_display_treated_only_dir; bool x_display_skipped; bool x_display_finished; infinint x_pause; bool x_empty_dir; compression x_compr_algo; U_I x_compression_level; U_I x_compression_block_size; infinint x_file_size; infinint x_first_file_size; mask * x_ea_mask; ///< points to a local copy of mask (must be allocated / releases by the archive_option_create objects) std::string x_execute; crypto_algo x_crypto; secu_string x_pass; U_32 x_crypto_size; std::vector x_gnupg_recipients; std::vector x_gnupg_signatories; mask * x_compr_mask; ///< points to a local copy of mask (must be allocated / releases by the archive_option_create objects) infinint x_min_compr_size; bool x_nodump; std::string exclude_by_ea; comparison_fields x_what_to_check; infinint x_hourshift; bool x_empty; bool x_alter_atime; bool x_old_alter_atime; ///< used to backup origina alter_atime value when activating furtive read mode bool x_furtive_read; bool x_same_fs; std::deque x_same_fs_include; std::deque x_same_fs_exclude; bool x_snapshot; bool x_cache_directory_tagging; infinint x_fixed_date; std::string x_slice_permission; std::string x_slice_user_ownership; std::string x_slice_group_ownership; infinint x_repeat_count; infinint x_repeat_byte; bool x_sequential_marks; infinint x_sparse_file_min_size; bool x_security_check; std::string x_user_comment; hash_algo x_hash; infinint x_slice_min_digits; mask * x_backup_hook_file_mask; std::string x_backup_hook_file_execute; bool x_ignore_unknown; std::shared_ptr x_entrepot; fsa_scope x_scope; U_I x_multi_threaded_crypto; U_I x_multi_threaded_compress; bool x_delta_diff; bool x_delta_signature; mask *x_delta_mask; bool has_delta_mask_been_set; infinint x_delta_sig_min_size; bool x_auto_zeroing_neg_dates; std::set x_ignored_as_symlink; modified_data_detection x_modified_data_detection; infinint x_iteration_count; hash_algo x_kdf_hash; delta_sig_block_size x_sig_block_len; void nullifyptr() noexcept; void destroy() noexcept; void copy_from(const archive_options_create & ref); void move_from(archive_options_create && ref) noexcept; void destroy_mask(mask * & ptr); void clean_mask(mask * & ptr); void check_mask(const mask & m); }; ///////////////////////////////////////////////////////// //////////// OPTIONS FOR ISOLATING A CATALOGUE ////////// ///////////////////////////////////////////////////////// /// class holding optional parameters used to isolate an existing archive class archive_options_isolate { public: archive_options_isolate(); archive_options_isolate(const archive_options_isolate & ref); archive_options_isolate(archive_options_isolate && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; archive_options_isolate & operator = (const archive_options_isolate & ref) { destroy(); copy_from(ref); return *this; }; archive_options_isolate & operator = (archive_options_isolate && ref) noexcept { move_from(std::move(ref)); return *this; }; ~archive_options_isolate() { destroy(); }; void clear(); ///////////////////////////////////////////////////////////////////// // setting methods /// whether overwritting is allowed void set_allow_over(bool allow_over) { x_allow_over = allow_over; }; /// whether a warning shall be issued before overwriting void set_warn_over(bool warn_over) { x_warn_over = warn_over; }; /// whether the user needs detailed output of the operation void set_info_details(bool info_details) { x_info_details = info_details; }; /// Pause beteween slices. Set to zero does not pause at all, set to 1 makes libdar pauses each slice, set to 2 makes libdar pause each 2 slices and so on. void set_pause(const infinint & pause) { x_pause = pause; }; /// the compression algorithm used void set_compression(compression algo) { x_algo = algo; }; /// the compression level (from 1 to 9) void set_compression_level(U_I compression_level) { x_compression_level = compression_level; }; /// set the compression block size void set_compression_block_size(U_I compression_block_size) { x_compression_block_size = compression_block_size; }; /// define the archive slicing /// \param[in] file_size set the slice size in byte (0 for a single slice whatever its size is) /// \param[in] first_file_size set the first file size /// \note if not specified or set to zero, first_file_size is considered equal to file_size void set_slicing(const infinint & file_size, const infinint & first_file_size = 0) { x_file_size = file_size; if(first_file_size.is_zero()) x_first_file_size = file_size; else x_first_file_size = first_file_size; }; /// command to execute after each slice creation /// several macros are available: /// - %%n : the number of the just created slice /// - %%N : the slice number with padded zeros according to slice_min_digits given option /// - %%b : the archive basename /// - %%p : the slices path /// - %%e : the archive extension (usually "dar") /// - %%c : the archive context ("init", "operation" or "last_slice") see dar(1) man page for more details /// - %%% : substitued by %% /// . void set_execute(const std::string & execute) { x_execute = execute; }; /// cypher to use void set_crypto_algo(crypto_algo crypto) { x_crypto = crypto; }; /// password / passphrase to encrypt the data with (empty string for interactive question) void set_crypto_pass(const secu_string & pass) { x_pass = pass; }; /// size of the encryption by block to use void set_crypto_size(U_32 crypto_size) { x_crypto_size = crypto_size; }; /// set the list of recipients that will be able to read the archive /// \note more details for the same option of archive_options_create /// \note since release 2.7.0 if a given std::string in the list contains an '@' the string is assumed to be an /// email and search is done in the keyring for that field type, else it is assumed to be a keyid. void set_gnupg_recipients(const std::vector & gnupg_recipients) { x_gnupg_recipients = gnupg_recipients; }; /// the private keys matching the email of the provided list are used to sign the archive random key /// \note since release 2.7.0 if a given std::string in the list contains an '@' the string is assumed to be an /// email and search is done in the keyring for that field type, else it is assumed to be a keyid. void set_gnupg_signatories(const std::vector & gnupg_signatories) { x_gnupg_signatories = gnupg_signatories; }; /// whether to make a dry-run operation void set_empty(bool empty) { x_empty = empty; }; /// if not an empty string set the slice permission according to the octal value given. void set_slice_permission(const std::string & slice_permission) { x_slice_permission = slice_permission; }; /// if not an empty string set the user ownership of slices accordingly void set_slice_user_ownership(const std::string & slice_user_ownership) { x_slice_user_ownership = slice_user_ownership; }; /// if not an empty string set the group ownership of slices accordingly void set_slice_group_ownership(const std::string & slice_group_ownership) { x_slice_group_ownership = slice_group_ownership; }; /// specify a user comment in the archive (always in clear text!) void set_user_comment(const std::string & comment) { x_user_comment = comment; }; /// specify whether to produce a hash file of the slice and which hash algo to use void set_hash_algo(hash_algo hash); /// defines the minimum digit a slice must have concerning its number, zeros will be prepended as much as necessary to respect this void set_slice_min_digits(infinint val) { x_slice_min_digits = val; }; /// whether to add escape sequence aka tape marks to allow sequential reading of the archive void set_sequential_marks(bool sequential) { x_sequential_marks = sequential; }; /// defines the protocol to use for slices void set_entrepot(const std::shared_ptr & entr) { if(!entr) throw Erange("archive_options_isolated::set_entrepot", "null entrepot pointer given in argument"); x_entrepot = entr; }; /// whether libdar is allowed to created several thread to work possibily faster on multicore CPU (require libthreadar) /// \deprecated this call is deprecated, see set_multi_threaded_*() more specific calls /// \note setting this to true is equivalent to calling set_mutli_threaded_crypto(2) void set_multi_threaded(bool val) { x_multi_threaded_crypto = 2; x_multi_threaded_compress = 2; }; /// how much thread libdar will use for cryptography (need libthreadar to be effective) void set_multi_threaded_crypto(U_I num) { x_multi_threaded_crypto = num; }; /// how much thread libdar will use for compression (need libthreadar too and compression_block_size > 0) void set_multi_threaded_compress(U_I num) { x_multi_threaded_compress = num; }; /// whether signature to base binary delta on the future has to be calculated and stored beside saved files void set_delta_signature(bool val) { x_delta_signature = val; }; /// whether to derogate to defaut delta file consideration while calculation delta signatures void set_delta_mask(const mask & delta_mask); /// whether to never calculate delta signature for files which size is smaller or equal to the given argument /// /// \note by default a min size of 10 kiB is used void set_delta_sig_min_size(const infinint & val) { x_delta_sig_min_size = val; }; /// key derivation void set_iteration_count(const infinint & val) { x_iteration_count = val; }; /// hash algo used for key derivation void set_kdf_hash(hash_algo algo) { x_kdf_hash = algo; }; /// block size to use to build delta signatures void set_sig_block_len(delta_sig_block_size val) { val.check(); x_sig_block_len = val; }; ///////////////////////////////////////////////////////////////////// // getting methods bool get_allow_over() const { return x_allow_over; }; bool get_warn_over() const { return x_warn_over; }; bool get_info_details() const { return x_info_details; }; const infinint & get_pause() const { return x_pause; }; compression get_compression() const { return x_algo; }; U_I get_compression_level() const { return x_compression_level; }; U_I get_compression_block_size() const { return x_compression_block_size; }; const infinint & get_slice_size() const { return x_file_size; }; const infinint & get_first_slice_size() const { return x_first_file_size; }; const std::string & get_execute() const { return x_execute; }; crypto_algo get_crypto_algo() const { return x_crypto; }; const secu_string & get_crypto_pass() const { return x_pass; }; U_32 get_crypto_size() const { return x_crypto_size; }; const std::vector & get_gnupg_recipients() const { return x_gnupg_recipients; }; const std::vector & get_gnupg_signatories() const { return x_gnupg_signatories; }; bool get_empty() const { return x_empty; }; const std::string & get_slice_permission() const { return x_slice_permission; }; const std::string & get_slice_user_ownership() const { return x_slice_user_ownership; }; const std::string & get_slice_group_ownership() const { return x_slice_group_ownership; }; const std::string & get_user_comment() const { return x_user_comment; }; hash_algo get_hash_algo() const { return x_hash; }; infinint get_slice_min_digits() const { return x_slice_min_digits; }; bool get_sequential_marks() const { return x_sequential_marks; }; const std::shared_ptr & get_entrepot() const { return x_entrepot; }; U_I get_multi_threaded_crypto() const { return x_multi_threaded_crypto; }; U_I get_multi_threaded_compress() const { return x_multi_threaded_compress; }; bool get_delta_signature() const { return x_delta_signature; }; const mask & get_delta_mask() const { return *x_delta_mask; } bool get_has_delta_mask_been_set() const { return has_delta_mask_been_set; }; const infinint & get_delta_sig_min_size() const { return x_delta_sig_min_size; }; const infinint & get_iteration_count() const { return x_iteration_count; }; hash_algo get_kdf_hash() const { return x_kdf_hash; }; delta_sig_block_size get_sig_block_len() const { return x_sig_block_len; }; private: bool x_allow_over; bool x_warn_over; bool x_info_details; infinint x_pause; compression x_algo; U_I x_compression_level; U_I x_compression_block_size; infinint x_file_size; infinint x_first_file_size; std::string x_execute; crypto_algo x_crypto; secu_string x_pass; U_32 x_crypto_size; std::vector x_gnupg_recipients; std::vector x_gnupg_signatories; bool x_empty; std::string x_slice_permission; std::string x_slice_user_ownership; std::string x_slice_group_ownership; std::string x_user_comment; hash_algo x_hash; infinint x_slice_min_digits; bool x_sequential_marks; std::shared_ptr x_entrepot; U_I x_multi_threaded_crypto; U_I x_multi_threaded_compress; bool x_delta_signature; mask *x_delta_mask; bool has_delta_mask_been_set; infinint x_delta_sig_min_size; infinint x_iteration_count; hash_algo x_kdf_hash; delta_sig_block_size x_sig_block_len; void copy_from(const archive_options_isolate & ref); void move_from(archive_options_isolate && ref) noexcept; void destroy() noexcept; void nullifyptr() noexcept; }; ///////////////////////////////////////////////////////// ////////// OPTONS FOR MERGING ARCHIVES ////////////////// ///////////////////////////////////////////////////////// /// class holding optional parameters used to proceed to the merge operation class archive_options_merge { public: archive_options_merge() { nullifyptr(); clear(); }; archive_options_merge(const archive_options_merge & ref) { copy_from(ref); }; archive_options_merge(archive_options_merge && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; archive_options_merge & operator = (const archive_options_merge & ref) { destroy(); copy_from(ref); return *this; }; archive_options_merge & operator = (archive_options_merge && ref) noexcept { move_from(std::move(ref)); return *this; }; ~archive_options_merge() { destroy(); }; void clear(); ///////////////////////////////////////////////////////////////////// // setting methods void set_auxiliary_ref(std::shared_ptr ref) { x_ref = ref; }; /// defines the filenames to only save (except directory) as those that match the given mask void set_selection(const mask & selection); /// defines the directories and files to consider /// \note WARNING: The filter mechanism used here is common to all operations and works /// by comparing full paths, while in the other hand, paths stored in the libdar archive are /// all relative to what was provided as fs_root at backup time. To have this filter mecanism /// working in the context of merging, where no fs_root can be provided to transform relative /// paths to absolute paths, libdar will emulate an fs_root with the pseudo root value of the /// path::FAKE_ROOT field. This is invisible form the libdar API user except when relying on /// mask_list objects, which cannot thus receive full path as those would not be prepended /// by path::FAKE_ROOT as fs_root value, because fs_root prefixing is only performed for relative /// paths. void set_subtree(const mask & subtree); /// defines whether overwritting is allowed or not void set_allow_over(bool allow_over) { x_allow_over = allow_over; }; /// defines whether a warning shall be issued before overwriting void set_warn_over(bool warn_over) { x_warn_over = warn_over; }; /// policy to solve merging conflict void set_overwriting_rules(const crit_action & overwrite); /// defines whether the user needs detailed output of the operation /// \note in API 5.5.x and before this switch drove the displaying /// of processing messages and treated files. now it only drives the /// display of processing messages, use set_display_treated to define /// whether files under treatement should be display or not void set_info_details(bool info_details) { x_info_details = info_details; }; /// defines whether to show treated files /// \param[in] display_treated true to display processed inodes /// \param[in] only_dir only display the current directory under processing, not its individual files void set_display_treated(bool display_treated, bool only_dir) { x_display_treated = display_treated; x_display_treated_only_dir = only_dir; }; /// whether to display files that have been excluded by filters void set_display_skipped(bool display_skipped) { x_display_skipped = display_skipped; }; /// set a pause beteween slices. Set to zero does not pause at all, set to 1 makes libdar pauses each slice, set to 2 makes libdar pause each 2 slices and so on. void set_pause(const infinint & pause) { x_pause = pause; }; /// defines whether we need to store ignored directories as empty void set_empty_dir(bool empty_dir) { x_empty_dir = empty_dir; }; /// set the compression algorithm to be used void set_compression(compression compr_algo) { x_compr_algo = compr_algo; }; /// set the compression level (from 1 to 9) void set_compression_level(U_I compression_level) { x_compression_level = compression_level; }; /// set the compression block size (0 for streamed compression) void set_compression_block_size(U_I compression_block_size) { x_compression_block_size = compression_block_size; }; /// define the archive slicing /// \param[in] file_size set the slice size in byte (0 for a single slice whatever its size is) /// \param[in] first_file_size set the first file size /// \note if not specified or set to zero, first_file_size is considered equal to file_size void set_slicing(const infinint & file_size, const infinint & first_file_size = 0) { x_file_size = file_size; if(first_file_size.is_zero()) x_first_file_size = file_size; else x_first_file_size = first_file_size; }; /// defines which Extended Attributes to save void set_ea_mask(const mask & ea_mask); /// set the command to execute after each slice creation /// several macros are available: /// - %%n : the number of the just created slice /// - %%N : the slice number with padded zeros according to slice_min_digits given option /// - %%b : the archive basename /// - %%p : the slices path /// - %%e : the archive extension (usually "dar") /// - %%c : the archive context ("init", "operation" or "last_slice") see dar(1) man page for more details /// - %%% : substitued by %% /// . void set_execute(const std::string & execute) { x_execute = execute; }; /// set the cypher to use void set_crypto_algo(crypto_algo crypto) { x_crypto = crypto; }; /// set the pass the password / passphrase to use. Giving an empty string makes the password asked /// interactively through the dialog argument if encryption has been set. void set_crypto_pass(const secu_string & pass) { x_pass = pass; }; /// set the size of the encryption by block to use void set_crypto_size(U_32 crypto_size) { x_crypto_size = crypto_size; }; /// set the list of recipients that will be able to read the archive /// \note more details for the same option of archive_options_create /// \note since release 2.7.0 if a given std::string in the list contains an '@' the string is assumed to be an /// email and search is done in the keyring for that field type, else it is assumed to be a keyid. void set_gnupg_recipients(const std::vector & gnupg_recipients) { x_gnupg_recipients = gnupg_recipients; }; /// the private keys matching the email of the provided list are used to sign the archive random key /// \note since release 2.7.0 if a given std::string in the list contains an '@' the string is assumed to be an /// email and search is done in the keyring for that field type, else it is assumed to be a keyid. void set_gnupg_signatories(const std::vector & gnupg_signatories) { x_gnupg_signatories = gnupg_signatories; }; /// defines files to compress void set_compr_mask(const mask & compr_mask); /// defines file size under which to never compress void set_min_compr_size(const infinint & min_compr_size) { x_min_compr_size = min_compr_size; }; /// defines whether we do a dry-run execution void set_empty(bool empty) { x_empty = empty; }; /// make dar ignore the 'algo' argument and do not uncompress / compress files that are selected for merging void set_keep_compressed(bool keep_compressed) { x_keep_compressed = keep_compressed; }; /// if not an empty string set the slice permission according to the octal value given. void set_slice_permission(const std::string & slice_permission) { x_slice_permission = slice_permission; }; /// if not an empty string set the user ownership of slices accordingly void set_slice_user_ownership(const std::string & slice_user_ownership) { x_slice_user_ownership = slice_user_ownership; }; /// if not an empty string set the group ownership of slices accordingly void set_slice_group_ownership(const std::string & slice_group_ownership) { x_slice_group_ownership = slice_group_ownership; }; /// if set to true use a merging mode suitable to build a decremental backup from two full backups (see Notes) void set_decremental_mode(bool mode) { x_decremental = mode; }; /// whether to activate escape sequence aka tape marks to allow sequential reading of the archive void set_sequential_marks(bool sequential) { x_sequential_marks = sequential; }; /// whether to try to detect sparse files void set_sparse_file_min_size(const infinint & size) { x_sparse_file_min_size = size; }; /// specify a user comment in the archive (always in clear text!) void set_user_comment(const std::string & comment) { x_user_comment = comment; }; /// specify whether to produce a hash file of the slice and which hash algo to use void set_hash_algo(hash_algo hash); /// defines the minimum digit a slice must have concerning its number, zeros will be prepended as much as necessary to respect this void set_slice_min_digits(infinint val) { x_slice_min_digits = val; }; /// defines the protocol to use for slices void set_entrepot(const std::shared_ptr & entr) { if(!entr) throw Erange("archive_options_merge::set_entrepot", "null entrepot pointer given in argument"); x_entrepot = entr; }; /// defines the FSA (Filesystem Specific Attribute) to only consider (by default all FSA are considered) void set_fsa_scope(const fsa_scope & scope) { x_scope = scope; }; /// whether libdar is allowed to spawn several threads to possibily work faster on multicore CPU (requires libthreadar) /// \deprecated this call is deprecated, see set_multi_threaded_*() more specific calls /// \note setting this to true is equivalent to calling set_mutli_threaded_crypto(2) void set_multi_threaded(bool val) { x_multi_threaded_crypto = 2; x_multi_threaded_compress = 2; }; /// how much thread libdar will use for cryptography (need libthreadar to be effective) void set_multi_threaded_crypto(U_I num) { x_multi_threaded_crypto = num; }; /// how much thread libdar will use for compression (need libthreadar too and compression_block_size > 0) void set_multi_threaded_compress(U_I num) { x_multi_threaded_compress = num; }; /// whether signature to base binary delta on the future has to be calculated and stored beside saved files /// \note the default is true, which lead to preserve delta signature over merging, but not to calculate new ones /// unless a mask is given to set_delta_mask() in which case signature are dropped / preserved / added in regard to /// this mask. If set_delta_signature() is false, providing a mask has no effect, no signature will be transfered nor /// calculated in the resulting merged archive. void set_delta_signature(bool val) { x_delta_signature = val; }; /// whether to derogate to defaut delta file consideration while calculation delta signatures void set_delta_mask(const mask & delta_mask); /// whether to never calculate delta signature for files which size is smaller or equal to the given argument /// \note by default a min size of 10 kiB is used void set_delta_sig_min_size(const infinint & val) { x_delta_sig_min_size = val; }; /// key derivation void set_iteration_count(const infinint & val) { x_iteration_count = val; }; /// hash algo used for key derivation void set_kdf_hash(hash_algo algo) { x_kdf_hash = algo; }; /// block size to use to build delta signatures void set_sig_block_len(delta_sig_block_size val) { val.check(); x_sig_block_len = val; }; ///////////////////////////////////////////////////////////////////// // getting methods std::shared_ptr get_auxiliary_ref() const { return x_ref; }; const mask & get_selection() const { if(x_selection == nullptr) throw SRC_BUG; return *x_selection; }; const mask & get_subtree() const { if(x_subtree == nullptr) throw SRC_BUG; return *x_subtree; }; bool get_allow_over() const { return x_allow_over; }; bool get_warn_over() const { return x_warn_over; }; const crit_action & get_overwriting_rules() const { if(x_overwrite == nullptr) throw SRC_BUG; return *x_overwrite; }; bool get_info_details() const { return x_info_details; }; bool get_display_treated() const { return x_display_treated; }; bool get_display_treated_only_dir() const { return x_display_treated_only_dir; }; bool get_display_skipped() const { return x_display_skipped; }; const infinint & get_pause() const { return x_pause; }; bool get_empty_dir() const { return x_empty_dir; }; compression get_compression() const { return x_compr_algo; }; U_I get_compression_level() const { return x_compression_level; }; U_I get_compression_block_size() const { return x_compression_block_size; }; const infinint & get_slice_size() const { return x_file_size; }; const infinint & get_first_slice_size() const { return x_first_file_size; }; const mask & get_ea_mask() const { if(x_ea_mask == nullptr) throw SRC_BUG; return *x_ea_mask; }; const std::string & get_execute() const { return x_execute; }; crypto_algo get_crypto_algo() const { return x_crypto; }; const secu_string & get_crypto_pass() const { return x_pass; }; U_32 get_crypto_size() const { return x_crypto_size; }; const std::vector & get_gnupg_recipients() const { return x_gnupg_recipients; }; const std::vector & get_gnupg_signatories() const { return x_gnupg_signatories; }; const mask & get_compr_mask() const { if(x_compr_mask == nullptr) throw SRC_BUG; return *x_compr_mask; }; const infinint & get_min_compr_size() const { return x_min_compr_size; }; bool get_empty() const { return x_empty; }; bool get_keep_compressed() const { return x_keep_compressed; }; const std::string & get_slice_permission() const { return x_slice_permission; }; const std::string & get_slice_user_ownership() const { return x_slice_user_ownership; }; const std::string & get_slice_group_ownership() const { return x_slice_group_ownership; }; bool get_decremental_mode() const { return x_decremental; }; bool get_sequential_marks() const { return x_sequential_marks; }; infinint get_sparse_file_min_size() const { return x_sparse_file_min_size; }; const std::string & get_user_comment() const { return x_user_comment; }; hash_algo get_hash_algo() const { return x_hash; }; infinint get_slice_min_digits() const { return x_slice_min_digits; }; const std::shared_ptr & get_entrepot() const { return x_entrepot; }; const fsa_scope & get_fsa_scope() const { return x_scope; }; U_I get_multi_threaded_crypto() const { return x_multi_threaded_crypto; }; U_I get_multi_threaded_compress() const { return x_multi_threaded_compress; }; bool get_delta_signature() const { return x_delta_signature; }; const mask & get_delta_mask() const { return *x_delta_mask; } bool get_has_delta_mask_been_set() const { return has_delta_mask_been_set; }; const infinint & get_delta_sig_min_size() const { return x_delta_sig_min_size; }; const infinint & get_iteration_count() const { return x_iteration_count; }; hash_algo get_kdf_hash() const { return x_kdf_hash; }; delta_sig_block_size get_sig_block_len() const { return x_sig_block_len; }; private: std::shared_ptr x_ref; mask * x_selection; mask * x_subtree; bool x_allow_over; bool x_warn_over; crit_action * x_overwrite; bool x_info_details; bool x_display_treated; bool x_display_treated_only_dir; bool x_display_skipped; infinint x_pause; bool x_empty_dir; compression x_compr_algo; U_I x_compression_level; U_I x_compression_block_size; infinint x_file_size; infinint x_first_file_size; mask * x_ea_mask; std::string x_execute; crypto_algo x_crypto; secu_string x_pass; U_32 x_crypto_size; std::vector x_gnupg_recipients; std::vector x_gnupg_signatories; mask * x_compr_mask; infinint x_min_compr_size; bool x_empty; bool x_keep_compressed; std::string x_slice_permission; std::string x_slice_user_ownership; std::string x_slice_group_ownership; bool x_decremental; bool x_sequential_marks; infinint x_sparse_file_min_size; std::string x_user_comment; hash_algo x_hash; infinint x_slice_min_digits; std::shared_ptr x_entrepot; fsa_scope x_scope; U_I x_multi_threaded_crypto; U_I x_multi_threaded_compress; bool x_delta_signature; mask *x_delta_mask; bool has_delta_mask_been_set; infinint x_delta_sig_min_size; infinint x_iteration_count; hash_algo x_kdf_hash; delta_sig_block_size x_sig_block_len; void destroy() noexcept; void copy_from(const archive_options_merge & ref); void move_from(archive_options_merge && ref) noexcept; void nullifyptr() noexcept; }; ///////////////////////////////////////////////////////// ////////// OPTONS FOR EXTRACTING FILES FROM ARCHIVE ///// ///////////////////////////////////////////////////////// /// class holding optional parameters used to extract files from an existing archive class archive_options_extract { public: enum t_dirty { dirty_ignore, dirty_warn, dirty_ok }; archive_options_extract() { nullifyptr(); clear(); }; archive_options_extract(const archive_options_extract & ref) { copy_from(ref); }; archive_options_extract(archive_options_extract && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; archive_options_extract & operator = (const archive_options_extract & ref) { destroy(); copy_from(ref); return *this; }; archive_options_extract & operator = (archive_options_extract && ref) noexcept { move_from(std::move(ref)); return *this; }; ~archive_options_extract() { destroy(); }; void clear(); ///////////////////////////////////////////////////////////////////// // setting methods /// defines the filenames to only save (except directory) as those that match the given mask void set_selection(const mask & selection); /// defines the directories and files to consider /// \note WARNING: The filter mechanism used here is common to all operations and works /// by comparing full paths, while in the other hand, paths stored in the libdar archive are /// all relative paths to what was provided as fs_root at backup time. To have this filter mecanism /// working in the context of restoration, libdar prepends the relative path found in the libdar /// archive with the fs_root argument given to archive::op_extract. /// In consequence the provided filter here, for extraction operation should be build taking into /// account that files to restore will be seen as subdirectories of this provided "fs_root" where /// the data will be restored. In other words, if the subtree mask do not accept anything under /// fs_root path, the resulting backup will be empty. void set_subtree(const mask & subtree); /// defines whether a warning shall be issued before overwriting void set_warn_over(bool warn_over) { x_warn_over = warn_over; }; /// defines whether the user needs detailed output of the operation /// /// \note in API 5.5.x and before this switch drove the displaying /// of processing messages and treated files. now it only drives the /// display of processing messages, use set_display_treated to define /// whether files under treatement should be display or not void set_info_details(bool info_details) { x_info_details = info_details; }; /// defines whether to show treated files /// \param[in] display_treated true to display processed inodes /// \param[in] only_dir only display the current directory under processing, not its individual files void set_display_treated(bool display_treated, bool only_dir) { x_display_treated = display_treated; x_display_treated_only_dir = only_dir; }; /// whether to display files that have been excluded by filters void set_display_skipped(bool display_skipped) { x_display_skipped = display_skipped; }; /// defines which Extended Attributes to save void set_ea_mask(const mask & ea_mask); /// whether to ignore directory structure and restore all files in the same directory void set_flat(bool flat) { x_flat = flat; }; /// fields to consider when comparing inodes with those on filesystem to determine if it is more recent (see comparison_fields enumeration), also defines whether mtime has to be restored (cf_mtime) whether permission have to be too (cf_ignore_owner) whether ownership has to be restored too (cf_all) void set_what_to_check(comparison_fields what_to_check) { x_what_to_check = what_to_check; }; /// whether a warning must be issue if a file to remove does not match the expected type of file void set_warn_remove_no_match(bool warn_remove_no_match) { x_warn_remove_no_match = warn_remove_no_match; }; /// defines whether we need to store ignored directories as empty void set_empty(bool empty) { x_empty = empty; }; /// whether to restore directories where no file has been triggered for backup (no file/inode change, excluded files,...) void set_empty_dir(bool empty_dir) { x_empty_dir = empty_dir; }; /// whether to restore dirty files (those that changed during backup), warn before restoring or ignoring them ///\param[in] ignore if set to true, dirty files are not restored at all ///\param[in] warn useless if ignored is false. If warn is true, a warning is issued before restoring each dirty file (default behavior) void set_dirty_behavior(bool ignore, bool warn) { x_dirty = ignore ? dirty_ignore : (warn ? dirty_warn : dirty_ok); }; /// alternative method to modify dirty behavior void set_dirty_behavior(t_dirty val) { x_dirty = val; }; /// overwriting policy void set_overwriting_rules(const crit_action & over); /// only consider deleted files (if set, no data get restored) /// \note setting both set_only_deleted() and set_ignore_deleted() will not restore anything, almost equivalent to a dry-run execution void set_only_deleted(bool val) { x_only_deleted = val; }; /// do not consider deleted files (if set, no inode will be removed) /// \note setting both set_only_deleted() and set_ignore_deleted() will not restore anything, almost equivalent to a dry-run execution void set_ignore_deleted(bool val) { x_ignore_deleted = val; }; /// defines the FSA (Filesystem Specific Attribute) to only consider (by default all FSA activated at compilation time are considered) void set_fsa_scope(const fsa_scope & scope) { x_scope = scope; }; /// whether to ignore unix sockets while restoring void set_ignore_unix_sockets(bool val) { x_ignore_unix_sockets = val; }; /// whether to ignore fs_root and use in-place path stored in the archive void set_in_place(bool arg) { x_in_place = arg; }; ///////////////////////////////////////////////////////////////////// // getting methods const mask & get_selection() const { if(x_selection == nullptr) throw SRC_BUG; return *x_selection; }; const mask & get_subtree() const { if(x_subtree == nullptr) throw SRC_BUG; return *x_subtree; }; bool get_warn_over() const { return x_warn_over; }; bool get_info_details() const { return x_info_details; }; bool get_display_treated() const { return x_display_treated; }; bool get_display_treated_only_dir() const { return x_display_treated_only_dir; }; bool get_display_skipped() const { return x_display_skipped; }; const mask & get_ea_mask() const { if(x_ea_mask == nullptr) throw SRC_BUG; return *x_ea_mask; }; bool get_flat() const { return x_flat; }; comparison_fields get_what_to_check() const { return x_what_to_check; }; bool get_warn_remove_no_match() const { return x_warn_remove_no_match; }; bool get_empty() const { return x_empty; }; bool get_empty_dir() const { return x_empty_dir; }; t_dirty get_dirty_behavior() const { return x_dirty; } const crit_action & get_overwriting_rules() const { if(x_overwrite == nullptr) throw SRC_BUG; return *x_overwrite; }; bool get_only_deleted() const { return x_only_deleted; }; bool get_ignore_deleted() const { return x_ignore_deleted; }; const fsa_scope & get_fsa_scope() const { return x_scope; }; bool get_ignore_unix_sockets() const { return x_ignore_unix_sockets; }; bool get_in_place() const { return x_in_place; }; private: mask * x_selection; mask * x_subtree; bool x_warn_over; bool x_info_details; bool x_display_treated; bool x_display_treated_only_dir; bool x_display_skipped; mask * x_ea_mask; bool x_flat; comparison_fields x_what_to_check; bool x_warn_remove_no_match; bool x_empty; bool x_empty_dir; t_dirty x_dirty; crit_action *x_overwrite; bool x_only_deleted; bool x_ignore_deleted; fsa_scope x_scope; bool x_ignore_unix_sockets; bool x_in_place; void destroy() noexcept; void nullifyptr() noexcept; void copy_from(const archive_options_extract & ref); void move_from(archive_options_extract && ref) noexcept; }; ///////////////////////////////////////////////////////// ////////// OPTIONS FOR LISTING AN ARCHIVE /////////////// ///////////////////////////////////////////////////////// /// class holding optional parameters used to list the contents of an existing archive class archive_options_listing { public: archive_options_listing() { nullifyptr(); clear(); }; archive_options_listing(const archive_options_listing & ref) { copy_from(ref); }; archive_options_listing(archive_options_listing && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; archive_options_listing & operator = (const archive_options_listing & ref) { destroy(); copy_from(ref); return *this; }; archive_options_listing & operator = (archive_options_listing && ref) noexcept { move_from(std::move(ref)); return *this; }; virtual ~archive_options_listing() { destroy(); }; virtual void clear(); ///////////////////////////////////////////////////////////////////// // setting methods /// whether output should be verbosed --> to be moved to shell output void set_info_details(bool info_details) { x_info_details = info_details; }; /// mask applied to filename, only those marching it will be listed /// \note this mask does not reject directory (it does not apply to it) void set_selection(const mask & selection); /// defines the directories and files to consider /// \note WARNING: The filter mechanism used here is common to all operations and works /// by comparing full paths, while in the other hand, paths stored in the libdar archive are /// all relative to what was provided as fs_root at backup time. To have this filter mecanism /// working in the context of listing, where no fs_root can be provided to transform relative /// paths to absolute paths, libdar will emulate an fs_root with the pseudo root value of the /// path::FAKE_ROOT field. This is invisible form the libdar API user except when relying on /// mask_list objects, which cannot thus receive full path as those would not be prepended /// by path::FAKE_ROOT as fs_root value, because fs_root prefixing is only performed for relative /// paths. void set_subtree(const mask & subtree); /// whether to only show entries that have their data fully saved void set_filter_unsaved(bool filter_unsaved) { x_filter_unsaved = filter_unsaved; }; /// whether to calculate the slice location of each file void set_slicing_location(bool val) { x_slicing_location = val; }; /// when slice location is performed, user may modify the slice layout of the archive /// \note this is needed for archive format older than 8 and when listing an /// isolated catalogue which original archive has been resized after the isolation /// operation. This option is not used if set_slicing_location is set to false void set_user_slicing(const infinint & slicing_first, const infinint & slicing_others); /// whether to fetch EA for listing /// \note this operation implies a lot of processing void set_display_ea(bool display_ea) { x_display_ea = display_ea; }; ///////////////////////////////////////////////////////////////////// // getting methods bool get_info_details() const { return x_info_details; }; const mask & get_selection() const; const mask & get_subtree() const; bool get_filter_unsaved() const { return x_filter_unsaved; }; bool get_user_slicing(infinint & slicing_first, infinint & slicing_others) const; bool get_slicing_location() const { return x_slicing_location; }; bool get_display_ea() const { return x_display_ea; }; private: bool x_info_details; mask * x_selection; mask * x_subtree; bool x_filter_unsaved; infinint *x_slicing_first; infinint *x_slicing_others; bool x_slicing_location; bool x_display_ea; void destroy() noexcept; void nullifyptr() noexcept; void copy_from(const archive_options_listing & ref); void move_from(archive_options_listing && ref) noexcept; }; ///////////////////////////////////////////////////////// ////////// OPTONS FOR DIFFING AN ARCHIVE //////////////// ///////////////////////////////////////////////////////// class archive_options_diff { public: archive_options_diff() { nullifyptr(); clear(); }; archive_options_diff(const archive_options_diff & ref) { copy_from(ref); }; archive_options_diff(archive_options_diff && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; archive_options_diff & operator = (const archive_options_diff & ref) { destroy(); copy_from(ref); return *this; }; archive_options_diff & operator = (archive_options_diff && ref) noexcept { move_from(std::move(ref)); return *this; }; ~archive_options_diff() { destroy(); }; void clear(); ///////////////////////////////////////////////////////////////////// // setting methods /// list of filenames to consider (directory not concerned by this fiter) void set_selection(const mask & selection); /// defines the directories and files to consider /// \note WARNING: The filter mechanism used here is common to all operations and works /// by comparing full paths, while in the other hand, paths stored in the libdar archive are /// all relative paths to what was provided as fs_root at backup time. To have this filter mecanism /// working in the context of restoration, libdar prepends the relative path found in the libdar /// archive with the fs_root argument given to archive::op_diff method. /// In consequence the provided filter here, for comparison operation should be build taking into /// account that files to compare will be seen as subdirectories of this provided "fs_root" the /// the archive will be compared to. In other words, if the subtree mask do not accept anything under /// the provided fs_root path, no file will be compared to what is on the filesystem. void set_subtree(const mask & subtree); /// whether the user needs detailed output of the operation /// /// \note in API 5.5.x and before this switch drove the displaying /// of processing messages and treated files. now it only drives the /// display of processing messages, use set_display_treated to define /// whether files under treatement should be display or not void set_info_details(bool info_details) { x_info_details = info_details; }; /// defines whether to show treated files /// \param[in] display_treated true to display processed inodes /// \param[in] only_dir only display the current directory under processing, not its individual files void set_display_treated(bool display_treated, bool only_dir) { x_display_treated = display_treated; x_display_treated_only_dir = only_dir; }; /// whether to display files that have been excluded by filters void set_display_skipped(bool display_skipped) { x_display_skipped = display_skipped; }; /// defines the Extended Attributes to compare void set_ea_mask(const mask & ea_mask); /// fields to consider wien comparing inodes with those on filesystem (see comparison_fields enumeration in catalogue.hpp) void set_what_to_check(comparison_fields what_to_check) { x_what_to_check = what_to_check; }; /// whether to alter atime or ctime in the filesystem when reading files to compare /// \param[in] alter_atime whether to change atime (true) or ctime (false) /// \note this parameter is used only when furtive_read_mode() is not activated void set_alter_atime(bool alter_atime) { if(x_furtive_read) x_old_alter_atime = alter_atime; else x_alter_atime = alter_atime; }; /// whether to use furtive read mode (if activated, alter_atime() has no meaning/use) void set_furtive_read_mode(bool furtive_read); /// ignore differences of at most this integer number of hours while looking for changes in dates void set_hourshift(const infinint & hourshift) { x_hourshift = hourshift; }; /// whether to compare symlink mtime (symlink mtime) void set_compare_symlink_date(bool compare_symlink_date) { x_compare_symlink_date = compare_symlink_date; }; /// defines the FSA (Filesystem Specific Attribute) to only consider (by default all FSA activated at compilation time are considered) void set_fsa_scope(const fsa_scope & scope) { x_scope = scope; }; /// whether to ignore fs_root and use in-place path stored in the archive void set_in_place(bool arg) { x_in_place = arg; }; ///////////////////////////////////////////////////////////////////// // getting methods const mask & get_selection() const { if(x_selection == nullptr) throw SRC_BUG; return *x_selection; }; const mask & get_subtree() const { if(x_subtree == nullptr) throw SRC_BUG; return *x_subtree; }; bool get_info_details() const { return x_info_details; }; bool get_display_treated() const { return x_display_treated; }; bool get_display_treated_only_dir() const { return x_display_treated_only_dir; }; bool get_display_skipped() const { return x_display_skipped; }; const mask & get_ea_mask() const { if(x_ea_mask == nullptr) throw SRC_BUG; return *x_ea_mask; }; comparison_fields get_what_to_check() const { return x_what_to_check; }; bool get_alter_atime() const { return x_alter_atime; }; bool get_furtive_read_mode() const { return x_furtive_read; }; const infinint & get_hourshift() const { return x_hourshift; }; bool get_compare_symlink_date() const { return x_compare_symlink_date; }; const fsa_scope & get_fsa_scope() const { return x_scope; }; bool get_in_place() const { return x_in_place; }; private: mask * x_selection; mask * x_subtree; bool x_info_details; bool x_display_treated; bool x_display_treated_only_dir; bool x_display_skipped; mask * x_ea_mask; comparison_fields x_what_to_check; bool x_alter_atime; bool x_old_alter_atime; bool x_furtive_read; infinint x_hourshift; bool x_compare_symlink_date; fsa_scope x_scope; bool x_in_place; void destroy() noexcept; void nullifyptr() noexcept; void copy_from(const archive_options_diff & ref); void move_from(archive_options_diff && ref) noexcept; }; ///////////////////////////////////////////////////////// ////////// OPTONS FOR TESTING AN ARCHIVE //////////////// ///////////////////////////////////////////////////////// /// class holding optional parameters used to test the structure coherence of an existing archive class archive_options_test { public: archive_options_test() { nullifyptr(); clear(); }; archive_options_test(const archive_options_test & ref) { copy_from(ref); }; archive_options_test(archive_options_test && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; archive_options_test & operator = (const archive_options_test & ref) { destroy(); copy_from(ref); return *this; }; archive_options_test & operator = (archive_options_test && ref) noexcept { move_from(std::move(ref)); return *this; }; ~archive_options_test() { destroy(); }; void clear(); ///////////////////////////////////////////////////////////////////// // setting methods /// list of filenames to consider (directory not concerned by this fiter) void set_selection(const mask & selection); /// defines the directories and files to consider /// \note WARNING: The filter mechanism used here is common to all operations and works /// by comparing full paths, while in the other hand, paths stored in the libdar archive are /// all relative to what was provided as fs_root at backup time. To have this filter mecanism /// working in the context of testing, where no fs_root can be provided to transform relative /// paths to absolute paths, libdar will emulate an fs_root with the pseudo root value of the /// path::FAKE_ROOT field. This is invisible form the libdar API user except when relying on /// mask_list objects, which cannot thus receive full path as those would not be prepended /// by path::FAKE_ROOT as fs_root value, because fs_root prefixing is only performed for relative /// paths. void set_subtree(const mask & subtree); /// whether the user needs detailed output of the operation /// /// \note in API 5.5.x and before this switch drove the displaying /// of processing messages and treated files. now it only drives the /// display of processing messages, use set_display_treated to define /// whether files under treatement should be display or not void set_info_details(bool info_details) { x_info_details = info_details; }; /// whether to display files that have been excluded by filters void set_display_skipped(bool display_skipped) { x_display_skipped = display_skipped; }; /// defines whether to show treated files /// \param[in] display_treated true to display processed inodes /// \param[in] only_dir only display the current directory under processing, not its individual files void set_display_treated(bool display_treated, bool only_dir) { x_display_treated = display_treated; x_display_treated_only_dir = only_dir; }; /// dry-run exectution if set to true void set_empty(bool empty) { x_empty = empty; }; ///////////////////////////////////////////////////////////////////// // getting methods const mask & get_selection() const { if(x_selection == nullptr) throw SRC_BUG; return *x_selection; }; const mask & get_subtree() const { if(x_subtree == nullptr) throw SRC_BUG; return *x_subtree; }; bool get_info_details() const { return x_info_details; }; bool get_display_treated() const { return x_display_treated; }; bool get_display_treated_only_dir() const { return x_display_treated_only_dir; }; bool get_display_skipped() const { return x_display_skipped; }; bool get_empty() const { return x_empty; }; private: mask * x_selection; mask * x_subtree; bool x_info_details; bool x_display_treated; bool x_display_treated_only_dir; bool x_display_skipped; bool x_empty; void destroy() noexcept; void nullifyptr() noexcept; void copy_from(const archive_options_test & ref); void move_from(archive_options_test && ref) noexcept; }; ///////////////////////////////////////////////////////// ///////// OPTIONS FOR REPAIRING AN ARCHIVE ////////////// ///////////////////////////////////////////////////////// /// class holding optional parameters used to create an archive class archive_options_repair { public: // default constructors and destructor. archive_options_repair(); archive_options_repair(const archive_options_repair & ref); archive_options_repair(archive_options_repair && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; archive_options_repair & operator = (const archive_options_repair & ref) { copy_from(ref); return *this; }; archive_options_repair & operator = (archive_options_repair && ref) noexcept { move_from(std::move(ref)); return *this; }; ~archive_options_repair() = default; ///////////////////////////////////////////////////////////////////// // set back to default (this is the state just after the object is constructed // this method is to be used to reuse a given object /// reset all the options to their default values void clear(); ///////////////////////////////////////////////////////////////////// // setting methods /// defines whether overwritting is allowed or not void set_allow_over(bool allow_over) { x_allow_over = allow_over; }; /// defines whether a warning shall be issued before overwriting void set_warn_over(bool warn_over) { x_warn_over = warn_over; }; /// defines whether the user needs detailed output of the operation /// /// \note in API 5.5.x and before this switch drove the displaying /// of processing messages and treated files. now it only drives the /// display of processing messages, use set_display_treated to define /// whether files under treatement should be display or not void set_info_details(bool info_details) { x_info_details = info_details; }; /// defines whether to show treated files /// /// \param[in] display_treated true to display processed inodes /// \param[in] only_dir only display the current directory under process, not its individual files void set_display_treated(bool display_treated, bool only_dir) { x_display_treated = display_treated; x_display_treated_only_dir = only_dir; }; /// whether to display files that have been excluded by filters void set_display_skipped(bool display_skipped) { x_display_skipped = display_skipped; }; /// whether to display a summary for each completed directory with total saved data and compression ratio void set_display_finished(bool display_finished) { x_display_finished = display_finished; }; /// set a pause beteween slices. Set to zero does not pause at all, set to 1 makes libdar pauses each slice, set to 2 makes libdar pause each 2 slices and so on. void set_pause(const infinint & pause) { x_pause = pause; }; /// define the archive slicing /// \param[in] file_size set the slice size in byte (0 for a single slice whatever its size is) /// \param[in] first_file_size set the first file size /// \note if not specified or set to zero, first_file_size is considered equal to file_size void set_slicing(const infinint & file_size, const infinint & first_file_size = 0) { x_file_size = file_size; if(first_file_size.is_zero()) x_first_file_size = file_size; else x_first_file_size = first_file_size; }; /// set the command to execute after each slice creation /// several macros are available: /// - %%n : the number of the just created slice /// - %%N : the slice number with padded zeros according to slice_min_digits given option /// - %%b : the archive basename /// - %%p : the slices path /// - %%e : the archive extension (usually "dar") /// - %%c : the archive context ("init", "operation" or "last_slice") see dar(1) man page for more details /// - %%% : substitued by %% /// . void set_execute(const std::string & execute) { x_execute = execute; }; /// set the cypher to use void set_crypto_algo(crypto_algo crypto) { x_crypto = crypto; }; /// set the pass the password / passphrase to use. Giving an empty string makes the password asked /// interactively through the dialog argument if encryption has been set. void set_crypto_pass(const secu_string & pass) { x_pass = pass; }; /// set the size of the encryption by block to use void set_crypto_size(U_32 crypto_size) { x_crypto_size = crypto_size; }; /// set the list of recipients that will be able to read the archive /// \note this is based on GnuPG keyring and assumes the user running libdar has its keyring /// containing for each recipient a valid public key. If a list of recipient is given the crypto_pass /// (see above) is not used, but the crypto_algo stays used to encrypt the archive using a randomly generated key /// which is encrypted using the public keys of the recipients and dropped that way encrypted inside the archive. /// \note if crypto_algo is not set while a list of recipient is given, the crypto algo will default to blowfish /// \note since release 2.7.0 if a given std::string in the list contains an '@' the string is assumed to be an /// email and search is done in the keyring for that field type, else it is assumed to be a keyid. void set_gnupg_recipients(const std::vector & gnupg_recipients) { x_gnupg_recipients = gnupg_recipients; }; /// the private keys matching the email of the provided list are used to sign the archive random key /// \note since release 2.7.0 if a given std::string in the list contains an '@' the string is assumed to be an /// email and search is done in the keyring for that field type, else it is assumed to be a keyid. void set_gnupg_signatories(const std::vector & gnupg_signatories) { x_gnupg_signatories = gnupg_signatories; }; /// whether to make a dry-run operation void set_empty(bool empty) { x_empty = empty; }; /// if not an empty string set the slice permission according to the octal value given. void set_slice_permission(const std::string & slice_permission) { x_slice_permission = slice_permission; }; /// if not an empty string set the user ownership of slices accordingly void set_slice_user_ownership(const std::string & slice_user_ownership) { x_slice_user_ownership = slice_user_ownership; }; /// if not an empty string set the group ownership of slices accordingly void set_slice_group_ownership(const std::string & slice_group_ownership) { x_slice_group_ownership = slice_group_ownership; }; /// specify a user comment in the archive (always in clear text!) void set_user_comment(const std::string & comment) { x_user_comment = comment; }; /// specify whether to produce a hash file of the slice and which hash algo to use /// \note the libdar::hash_algo data type is defined in hash_fichier.hpp, valid values /// are for examle libdar::hash_none, libdar::hash_md5, libdar::hash_sha1, libdar::hash_sha512... void set_hash_algo(hash_algo hash); /// defines the minimum digit a slice must have concerning its number, zeros will be prepended as much as necessary to respect this void set_slice_min_digits(infinint val) { x_slice_min_digits = val; }; /// defines the protocol to use for slices void set_entrepot(const std::shared_ptr & entr) { if(!entr) throw Erange("archive_options_repair::set_entrepot", "null entrepot pointer given in argument"); x_entrepot = entr; }; /// whether libdar is allowed to spawn several threads to possibily work faster on multicore CPU (requires libthreadar) /// \deprecated this call is deprecated, see set_multi_threaded_*() more specific calls /// \note setting this to true is equivalent to calling set_mutli_threaded_crypto(2) void set_multi_threaded(bool val) { x_multi_threaded_crypto = 2; x_multi_threaded_compress = 2; }; /// how much thread libdar will use for cryptography (need libthreadar to be effective) void set_multi_threaded_crypto(U_I num) { x_multi_threaded_crypto = num; }; /// how much thread libdar will use for compression (need libthreadar too and compression_block_size > 0) void set_multi_threaded_compress(U_I num) { x_multi_threaded_compress = num; }; ///////////////////////////////////////////////////////////////////// // getting methods bool get_allow_over() const { return x_allow_over; }; bool get_warn_over() const { return x_warn_over; }; bool get_info_details() const { return x_info_details; }; bool get_display_treated() const { return x_display_treated; }; bool get_display_treated_only_dir() const { return x_display_treated_only_dir; }; bool get_display_skipped() const { return x_display_skipped; }; bool get_display_finished() const { return x_display_finished; }; const infinint & get_pause() const { return x_pause; }; const infinint & get_slice_size() const { return x_file_size; }; const infinint & get_first_slice_size() const { return x_first_file_size; }; const std::string & get_execute() const { return x_execute; }; crypto_algo get_crypto_algo() const { return x_crypto; }; const secu_string & get_crypto_pass() const { return x_pass; }; U_32 get_crypto_size() const { return x_crypto_size; }; const std::vector & get_gnupg_recipients() const { return x_gnupg_recipients; }; const std::vector & get_gnupg_signatories() const { return x_gnupg_signatories; }; bool get_empty() const { return x_empty; }; const std::string & get_slice_permission() const { return x_slice_permission; }; const std::string & get_slice_user_ownership() const { return x_slice_user_ownership; }; const std::string & get_slice_group_ownership() const { return x_slice_group_ownership; }; const std::string & get_user_comment() const { return x_user_comment; }; hash_algo get_hash_algo() const { return x_hash; }; infinint get_slice_min_digits() const { return x_slice_min_digits; }; const std::shared_ptr & get_entrepot() const { return x_entrepot; }; U_I get_multi_threaded_crypto() const { return x_multi_threaded_crypto; }; U_I get_multi_threaded_compress() const { return x_multi_threaded_compress; }; private: bool x_allow_over; bool x_warn_over; bool x_info_details; bool x_display_treated; bool x_display_treated_only_dir; bool x_display_skipped; bool x_display_finished; infinint x_pause; infinint x_file_size; infinint x_first_file_size; std::string x_execute; crypto_algo x_crypto; secu_string x_pass; U_32 x_crypto_size; std::vector x_gnupg_recipients; std::vector x_gnupg_signatories; bool x_empty; std::string x_slice_permission; std::string x_slice_user_ownership; std::string x_slice_group_ownership; std::string x_user_comment; hash_algo x_hash; infinint x_slice_min_digits; std::shared_ptr x_entrepot; U_I x_multi_threaded_crypto; U_I x_multi_threaded_compress; void nullifyptr() noexcept {}; void copy_from(const archive_options_repair & ref); void move_from(archive_options_repair && ref) noexcept; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_hard_link_write.hpp0000644000175000017500000001242714636066467017473 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filesystem_hard_link_write.hpp /// \brief class filesystem_hard_link_write keeps trace of already written inode to restore hard links /// \ingroup Private #ifndef FILESYSTEM_HARD_LINK_WRITE_HPP #define FILESYSTEM_HARD_LINK_WRITE_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_STAT_H #include #endif } // end extern "C" #include #include "catalogue.hpp" #include "infinint.hpp" #include "fsa_family.hpp" #include "cat_all_entrees.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// keep trace of already written inodes to restore hard links class filesystem_hard_link_write : protected mem_ui { // this class is not to be used directly // it only provides routines to its inherited classes public: filesystem_hard_link_write(const std::shared_ptr & dialog): mem_ui(dialog) { corres_write.clear(); }; filesystem_hard_link_write(const filesystem_hard_link_write & ref) = delete; filesystem_hard_link_write(filesystem_hard_link_write && ref) = delete; filesystem_hard_link_write & operator = (const filesystem_hard_link_write & ref) = delete; filesystem_hard_link_write & operator = (filesystem_hard_link_write && ref) = delete; ~filesystem_hard_link_write() = default; void write_hard_linked_target_if_not_set(const cat_mirage *ref, const std::string & chemin); // if a hard linked inode has not been restored (no change, or less recent than the one on filesystem) // it is necessary to inform filesystem, where to hard link on, any future hard_link // that could be necessary to restore. /// \return true if an inode in filesystem has been seen for that hard linked inode bool known_etiquette(const infinint & eti); /// forget everything about a hard link if the path used to build subsequent hard links is the one given in argument /// \param[in] ligne is the etiquette number for that hard link /// \param[in] path if the internaly recorded path to build subsequent hard link to that inode is equal to path, forget everything about this hard linked inode void clear_corres_if_pointing_to(const infinint & ligne, const std::string & path); protected: void corres_reset() { corres_write.clear(); }; /// generate inode or make a hard link on an already restored or existing inode. /// \param[in] ref object to restore in filesystem /// \param[in] ou where to restore it void make_file(const cat_nomme * ref, const path & ou); /// add the given EA matching the given mask to the file pointed to by "e" and spot /// \param[in] e may be an inode or a hard link to an inode, /// \param[in] list_ea the list of EA to restore /// \param[in] spot the path where to restore these EA (full path required, including the filename of 'e') /// \param[in] ea_mask the EA entry to restore from the list_ea (other entries are ignored) /// \return true if EA could be restored, false if "e" is a hard link to an inode that has its EA already restored previously /// \note the list_ea EA are restored to spot path, the object e is only here to validate that this operation /// has not already been done through another hard linked inode to that same inode bool raw_set_ea(const cat_nomme *e, const ea_attributs & list_ea, const std::string & spot, const mask & ea_mask); // check whether the inode for which to restore EA is not a hard link to // an already restored inode. if not, it calls the proper ea_filesystem call to restore EA /// remove EA set from filesystem's file, allows subsequent raw_set_ea /// \param[in] e this object may be a hard link to or an inode /// \param[in] path the path in the filesystem where reside the object whose EA to clear /// \return true if EA could be cleared, false if "e" is a hard link to an inode that has its EA already restored previously bool raw_clear_ea_set(const cat_nomme *e, const std::string & path); private: struct corres_ino_ea { std::string chemin; bool ea_restored; }; std::map corres_write; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_diff.hpp0000644000175000017500000000564714636066467015244 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filesystem_diff.hpp /// \brief class filesystem_diff makes a flow of inode to feed the difference filter routine /// \ingroup Private #ifndef FILESYSTEM_DIFF_HPP #define FILESYSTEM_DIFF_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_STAT_H #include #endif } // end extern "C" #include #include "cat_nomme.hpp" #include "filesystem_hard_link_read.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// make a flow of inode to feed the difference filter routine class filesystem_diff : public filesystem_hard_link_read { public: filesystem_diff(const std::shared_ptr & dialog, const path &root, bool x_info_details, const mask & x_ea_mask, bool alter_atime, bool furtive_read_mode, const fsa_scope & scope); filesystem_diff(const filesystem_diff & ref) = delete; filesystem_diff(filesystem_diff && ref) = delete; filesystem_diff & operator = (const filesystem_diff & ref) = delete; filesystem_diff & operator = (filesystem_diff && ref) = delete; ~filesystem_diff() { detruire(); }; void reset_read(); bool read_filename(const std::string & name, cat_nomme * &ref); // looks for a file of name given in argument, in current reading directory // if this is a directory, subsequent read take place in it void skip_read_filename_in_parent_dir(); // subsequent calls to read_filename will take place in parent directory. private: struct filename_struct { datetime last_acc; datetime last_mod; }; path *fs_root; bool info_details; mask *ea_mask; bool alter_atime; bool furtive_read_mode; path *current_dir; std::deque filename_pile; void detruire(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/parallel_block_compressor.cpp0000644000175000017500000006355514636067146017302 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "parallel_block_compressor.hpp" #include "erreurs.hpp" #include "compress_block_header.hpp" using namespace std; using namespace libthreadar; namespace libdar { ///////////////////////////////////////////////////// // // parallel_block_compressor class implementation // // parallel_block_compressor::parallel_block_compressor(U_I num_workers, unique_ptr block_zipper, generic_file & compressed_side, U_I uncompressed_bs): proto_compressor((compressed_side.get_mode() == gf_read_only)? gf_read_only: gf_write_only), num_w(num_workers), zipper(move(block_zipper)), compressed(&compressed_side), uncompressed_block_size(uncompressed_bs) { U_I compr_bs = zipper->get_min_size_to_compress(uncompressed_block_size); // sanity checks on fields set by constructors if(get_mode() == gf_read_write) throw SRC_BUG; // mode not suported for this type of object if(num_w < 1) throw SRC_BUG; if(!zipper) throw SRC_BUG; if(compressed == nullptr) throw SRC_BUG; if(uncompressed_block_size < min_uncompressed_block_size) throw SRC_BUG; // initializing simple fields not set by constructors suspended = false; running_threads = false; reof = false; // creating inter thread communication structures try { disperse = make_shared >(get_ratelier_size(num_w)); rassemble = make_shared >(get_ratelier_size(num_w)); tas = make_shared >(); // created empty } catch(std::bad_alloc & e) { throw Ememory("parallel_block_compressor::parallel_block_compressor"); } // now filling the head that was created empty for(U_I i = 0 ; i < get_heap_size(num_w) ; ++i) tas->put(make_unique(compr_bs, uncompressed_block_size)); // creating the zip_below_* thread object if(get_mode() == gf_write_only) { writer = make_unique(rassemble, compressed, tas, num_w); } else { reader = make_unique(compressed, disperse, tas, num_w); } // creating the worker threads objects for(U_I i = 0 ; i < num_w ; ++i) travailleurs.push_back(make_unique(disperse, rassemble, zipper->clone(), get_mode() == gf_write_only)); // no other thread than the one executing this code is running at this point!!! } parallel_block_compressor::~parallel_block_compressor() { try { terminate(); } catch(...) { // ignore all exceptions } } void parallel_block_compressor::suspend_compression() { if(get_mode() != gf_read_only) inherited_sync_write(); suspended = true; } void parallel_block_compressor::resume_compression() { suspended = false; } bool parallel_block_compressor::skippable(skippability direction, const infinint & amount) { if(is_terminated()) throw SRC_BUG; stop_threads(); return compressed->skippable(direction, amount); } bool parallel_block_compressor::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; stop_threads(); reof = false; return compressed->skip(pos); } bool parallel_block_compressor::skip_to_eof() { if(is_terminated()) throw SRC_BUG; stop_threads(); reof = false; return compressed->skip_to_eof(); } bool parallel_block_compressor::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; stop_threads(); reof = false; return skip_relative(x); } bool parallel_block_compressor::truncatable(const infinint & pos) const { parallel_block_compressor *me = const_cast(this); if(me == nullptr) throw SRC_BUG; if(is_terminated()) throw SRC_BUG; me->stop_threads(); return compressed->truncatable(pos); } infinint parallel_block_compressor::get_position() const { parallel_block_compressor *me = const_cast(this); if(me == nullptr) throw SRC_BUG; if(is_terminated()) throw SRC_BUG; me->stop_threads(); return compressed->get_position(); } U_I parallel_block_compressor::inherited_read(char *a, U_I size) { if(is_terminated()) throw SRC_BUG; if(suspended) { stop_threads(); if(!reof) return compressed->read(a, size); else return 0; } else { U_I ret = 0; if(! reof) run_threads(); while(ret < size && ! reof) { if(lus_data.empty()) rassemble->gather(lus_data, lus_flags); while(!lus_data.empty() && ret < size && ! reof) { if(lus_flags.empty()) throw SRC_BUG; switch(static_cast(lus_flags.front())) { case compressor_block_flags::data: ret += lus_data.front()->clear_data.read(a + ret, size - ret); if(lus_data.front()->clear_data.all_is_read()) { tas->put(move(lus_data.front())); lus_data.pop_front(); lus_flags.pop_front(); } break; case compressor_block_flags::eof_die: reof = true; stop_threads(); // this may throw a pending exception break; case compressor_block_flags::error: // error received from the zip_below_read thread // the thread has terminated and all worker have been // asked to terminate by the zip_below_read thread stop_threads(); // this should relaunch the exception from the worker throw SRC_BUG; // if not this is a bug case compressor_block_flags::worker_error: // a single worker have reported an error and is till alive // we drop this single order // to cleanly stop threads tas->put(move(lus_data.front())); lus_data.pop_front(); lus_flags.pop_front(); // no we can stop the threads poperly stop_threads(); // we stop all threads which will relaunch the worker exception throw SRC_BUG; // if not this is a bug default: throw SRC_BUG; } } } return ret; } } void parallel_block_compressor::inherited_write(const char *a, U_I size) { U_I wrote = 0; if(is_terminated()) throw SRC_BUG; if(suspended) { stop_threads(); compressed->write(a, size); } else { run_threads(); while(wrote < size && !writer->exception_pending()) { if(!curwrite) { curwrite = tas->get(); curwrite->reset(); } else { if(curwrite->clear_data.is_full()) throw SRC_BUG; } wrote += curwrite->clear_data.write(a + wrote, size - wrote); if(curwrite->clear_data.is_full()) { curwrite->clear_data.rewind_read(); disperse->scatter(curwrite, static_cast(compressor_block_flags::data)); } } if(writer->exception_pending()) { stop_threads(); // this should throw an exception // else we do it now: throw SRC_BUG; } } } void parallel_block_compressor::inherited_truncate(const infinint & pos) { if(is_terminated()) throw SRC_BUG; stop_threads(); compressed->truncate(pos); } void parallel_block_compressor::inherited_sync_write() { if(is_terminated()) throw SRC_BUG; if(curwrite && curwrite->clear_data.get_data_size() > 0) { run_threads(); disperse->scatter(curwrite, static_cast(compressor_block_flags::data)); } // this adds the eof mark (zero block size) stop_threads(); } void parallel_block_compressor::inherited_terminate() { switch(get_mode()) { case gf_read_only: break; case gf_write_only: inherited_sync_write(); break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } stop_threads(); } void parallel_block_compressor::send_flag_to_workers(compressor_block_flags flag) { unique_ptr ptr; if(get_mode() != gf_write_only) throw SRC_BUG; for(U_I i = 0; i < num_w; ++i) { ptr = tas->get(); disperse->scatter(ptr, static_cast(flag)); } } void parallel_block_compressor::stop_threads() { switch(get_mode()) { case gf_read_only: stop_read_threads(); break; case gf_write_only: stop_write_threads(); break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } } void parallel_block_compressor::stop_read_threads() { if(running_threads) { if(!reader) throw SRC_BUG; reader->do_stop(); switch(purge_ratelier_up_to_non_data()) { case compressor_block_flags::data: throw SRC_BUG; case compressor_block_flags::eof_die: break; case compressor_block_flags::error: break; case compressor_block_flags::worker_error: (void)purge_ratelier_up_to_non_data(); // need to purge further as this is // not a global order that equals the // number of workers break; default: throw SRC_BUG; } running_threads = false; // we must set this before join() // to avoid purging a second time // in case join() would throw an // exception reader->join(); for(deque >::iterator it = travailleurs.begin(); it != travailleurs.end(); ++it) { if((*it) != nullptr) (*it)->join(); else throw SRC_BUG; } } } void parallel_block_compressor::stop_write_threads() { if(curwrite && curwrite->clear_data.get_data_size() > 0) inherited_sync_write(); if(running_threads) { if(!writer) throw SRC_BUG; running_threads = false; // change the flag before calling join() // as they may trigger an exception if(writer->is_running()) { send_flag_to_workers(compressor_block_flags::eof_die); writer->join(); for(deque >::iterator it = travailleurs.begin(); it !=travailleurs.end(); ++it) { if((*it) != nullptr) (*it)->join(); else throw SRC_BUG; } } } } void parallel_block_compressor::run_threads() { switch(get_mode()) { case gf_read_only: run_read_threads(); break; case gf_write_only: run_write_threads(); break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } } void parallel_block_compressor::run_read_threads() { if(!running_threads) { if(!reader) throw SRC_BUG; if(reader->is_running()) throw SRC_BUG; reader->reset(); reader->run(); for(deque >::iterator it = travailleurs.begin(); it !=travailleurs.end(); ++it) { if((*it) != nullptr) (*it)->run(); else throw SRC_BUG; } running_threads = true; } } void parallel_block_compressor::run_write_threads() { if(!running_threads) { if(!writer) throw SRC_BUG; if(writer->is_running()) throw SRC_BUG; writer->reset(); writer->run(); for(deque >::iterator it = travailleurs.begin(); it !=travailleurs.end(); ++it) { if((*it) != nullptr) (*it)->run(); else throw SRC_BUG; } running_threads = true; } } compressor_block_flags parallel_block_compressor::purge_ratelier_up_to_non_data() { S_I expected = num_w; compressor_block_flags ret = compressor_block_flags::data; if(get_mode() != gf_read_only) throw SRC_BUG; while(expected > 0) { if(lus_data.empty()) { if(!lus_flags.empty()) throw SRC_BUG; rassemble->gather(lus_data, lus_flags); } while(!lus_flags.empty() && expected > 0) { if(lus_data.empty()) throw SRC_BUG; if(ret == compressor_block_flags::data && lus_flags.front() != static_cast(compressor_block_flags::data)) ret = static_cast(lus_flags.front()); if(lus_flags.front() == static_cast(ret) && ret != compressor_block_flags::data) { --expected; if(ret == compressor_block_flags::worker_error) expected = 0; } tas->put(move(lus_data.front())); lus_data.pop_front(); lus_flags.pop_front(); } } return ret; } U_I parallel_block_compressor::get_heap_size(U_I num_workers) { U_I ratelier_size = get_ratelier_size(num_workers); U_I heap_size = ratelier_size * 2 + num_workers + 1 + ratelier_size + 2; // each ratelier can be full of crypto_segment and at the same // time, each worker could hold a crypto_segment, the below thread // as well and the main thread for parallel_block_compressor could hold // a deque of the size of the ratelier plus 2 more crypto_segments return heap_size; } ///////////////////////////////////////////////////// // // zip_below_write class implementation // // zip_below_write::zip_below_write(const shared_ptr > & source, generic_file *dest, const shared_ptr > & xtas, U_I num_workers): src(source), dst(dest), tas(xtas), num_w(num_workers) { if(!src) throw SRC_BUG; if(dst == nullptr) throw SRC_BUG; if(!tas) throw SRC_BUG; if(num_w < 1) throw SRC_BUG; // dropping the initial uncompressed_block_size info reset(); } void zip_below_write::reset() { error = false; ending = num_w; tas->put(data); data.clear(); flags.clear(); } void zip_below_write::inherited_run() { try { work(); } catch(...) { error = true; // this should trigger the parallel_block_compressor // to push eof_die flag leading work() and zip_workers threads // to complete as properly as possible (minimizing dead-lock // situation). try { work(); } catch(...) { // do nothing } throw; // relaunching the exception now as we end the thread } } void zip_below_write::work() { do { if(data.empty()) { if(!flags.empty()) { if(!error) throw SRC_BUG; // does not hurt in error context // we must survive at any cost } src->gather(data, flags); } while(!data.empty() && ending > 0) { if(flags.empty()) { if(!error) throw SRC_BUG; } else { compress_block_header bh; switch(static_cast(flags.front())) { case compressor_block_flags::data: if(!error) { bh.type = compress_block_header::H_DATA; bh.size = data.front()->crypted_data.get_data_size(); bh.dump(*dst); dst->write(data.front()->crypted_data.get_addr(), data.front()->crypted_data.get_data_size()); } // else do nothing (avoid generating a new exception) pop_front(); break; case compressor_block_flags::eof_die: --ending; pop_front(); if(ending == 0) { bh.type = compress_block_header::H_EOF; bh.size = 0; bh.dump(*dst); } break; case compressor_block_flags::worker_error: // error received from a zip_worker error = true; // this will trigger the main thread to terminate all threads pop_front(); break; case compressor_block_flags::error: pop_front(); if(!error) throw SRC_BUG; break; default: pop_front(); if(!error) throw SRC_BUG; } } } } while(ending > 0); } ///////////////////////////////////////////////////// // // zip_below_read class implementation // // zip_below_read::zip_below_read(generic_file *source, const shared_ptr > & dest, const shared_ptr > & xtas, U_I num_workers): src(source), dst(dest), tas(xtas), num_w(num_workers) { if(src == nullptr) throw SRC_BUG; if(!dst) throw SRC_BUG; if(!tas) throw SRC_BUG; if(num_w < 1) throw SRC_BUG; reset(); } void zip_below_read::reset() { should_i_stop = false; if(ptr) tas->put(move(ptr)); } void zip_below_read::inherited_run() { try { work(); } catch(...) { if(ptr) tas->put(move(ptr)); push_flag_to_all_workers(compressor_block_flags::error); throw; // relaunching the exception now as we end the thread } } void zip_below_read::work() { bool end = false; U_I aux; compress_block_header bh; do { // reading compressed block's header if(!should_i_stop) { bh.set_from(*src); aux = 0; bh.size.unstack(aux); if(!bh.size.is_zero()) { // the read infinint does not hold in an a U_I assuming // data corruption occured throw Erange("zip_below_read::work", gettext("incoherent compressed block structure, compressed data corruption")); } } else // we are asked to stop by the parallel_block_compressor thread { aux = 0; // simulating an end of file bh.type = compress_block_header::H_EOF; } switch(bh.type) { case compress_block_header::H_EOF: push_flag_to_all_workers(compressor_block_flags::eof_die); if(aux != 0) throw Erange("zip_below_read::work", gettext("incoherent compressed block structure, compressed data corruption")); end = true; break; case compress_block_header::H_DATA: if(!ptr) { ptr = tas->get(); ptr->reset(); } if(aux > ptr->crypted_data.get_max_size()) { tas->put(move(ptr)); push_flag_to_all_workers(compressor_block_flags::error); end = true; } else { ptr->crypted_data.set_data_size(src->read(ptr->crypted_data.get_addr(), aux)); if(ptr->crypted_data.get_data_size() < aux) { // we should have the whole data filled, not less than aux! tas->put(move(ptr)); push_flag_to_all_workers(compressor_block_flags::error); end = true; } else { ptr->crypted_data.rewind_read(); dst->scatter(ptr, static_cast(compressor_block_flags::data)); } } break; default: throw Erange("zip_below_read::work", gettext("incoherent compressed block structure, compressed data corruption")); } } while(!end); } void zip_below_read::push_flag_to_all_workers(compressor_block_flags flag) { for(U_I i = 0; i < num_w; ++i) { if(!ptr) ptr = tas->get(); dst->scatter(ptr, static_cast(flag)); } } ///////////////////////////////////////////////////// // // zip_worker class implementation // // zip_worker::zip_worker(shared_ptr > & read_side, shared_ptr > & write_side, unique_ptr && ptr, bool compress): reader(read_side), writer(write_side), compr(move(ptr)), do_compress(compress) { if(!reader) throw SRC_BUG; if(!writer) throw SRC_BUG; if(!compr) throw SRC_BUG; error = false; } void zip_worker::inherited_run() { try { work(); } catch(...) { error = true; // this should trigger the parallel_block_compressor // to push eof_die flag leading work() and zip_workers threads // to complete as properly as possible (minimizing dead-lock // situation). try { signed int flag; if(!transit) transit = reader->worker_get_one(transit_slot, flag); writer->worker_push_one(transit_slot, transit, static_cast(compressor_block_flags::worker_error)); work(); } catch(...) { // do nothing } throw; // relaunching the exception now as we end the thread } } void zip_worker::work() { bool ending = false; signed int flag; do { if(!transit) transit = reader->worker_get_one(transit_slot, flag); switch(static_cast(flag)) { case compressor_block_flags::data: if(!error) { if(do_compress) { transit->crypted_data.set_data_size(compr->compress_data(transit->clear_data.get_addr(), transit->clear_data.get_data_size(), transit->crypted_data.get_addr(), transit->crypted_data.get_max_size())); transit->crypted_data.rewind_read(); } else { transit->clear_data.set_data_size(compr->uncompress_data(transit->crypted_data.get_addr(), transit->crypted_data.get_data_size(), transit->clear_data.get_addr(), transit->clear_data.get_max_size())); transit->clear_data.rewind_read(); } } writer->worker_push_one(transit_slot, transit, flag); break; case compressor_block_flags::eof_die: ending = true; writer->worker_push_one(transit_slot, transit, flag); break; case compressor_block_flags::error: if(!do_compress) { writer->worker_push_one(transit_slot, transit, flag); ending = true; // in read mode (uncompressing) in case the zip_below_write // thread sends an error message, it ends, so we propagate // and terminate too, but we do not throw exception ourselves } else { if(!error) throw SRC_BUG; // in write mode (compressing) we should never receive a error // flag from the main thread } break; case compressor_block_flags::worker_error: if(!error) throw SRC_BUG; // only a worker should set this error and we // do not communicate with other workers else writer->worker_push_one(transit_slot, transit, flag); // we now stay as much transparent as possible break; default: if(!error) throw SRC_BUG; else writer->worker_push_one(transit_slot, transit, flag); // we now stay as much transparent as possible break; } } while(!ending); } } // end of namespace dar-2.7.15/src/libdar/get_version.hpp0000644000175000017500000001444714640024724014373 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file get_version.hpp /// \brief routine to initialize libdar and manage its running threads /// \ingroup API #ifndef GET_VERSION_HPP #define GET_VERSION_HPP #include "../my_config.h" extern "C" { #if MUTEX_WORKS #if HAVE_PTHREAD_H #include #endif #endif } #include #include "integers.hpp" /// libdar namespace encapsulate all libdar symbols namespace libdar { /// \addtogroup API /// @{ /// libdar Major version defined at compilation time constexpr U_I LIBDAR_COMPILE_TIME_MAJOR = 6; /// libdar Medium version defined at compilation time constexpr U_I LIBDAR_COMPILE_TIME_MEDIUM = 7; /// libdar Minor version defined at compilation time constexpr U_I LIBDAR_COMPILE_TIME_MINOR = 3; //////////////////////////////////////////////////////////////////////// // LIBDAR INITIALIZATION METHODS // // // // A FUNCTION OF THE get_version*() FAMILY *MUST* BE CALLED // // BEFORE ANY OTHER FUNCTION OF THIS LIBRARY // // // // CLIENT PROGRAM MUST CHECK THAT THE MAJOR NUMBER RETURNED // // BY THIS CALL IS NOT GREATER THAN THE VERSION USED AT COMPILATION // // TIME. IF SO, THE PROGRAM MUST ABORT AND RETURN A WARNING TO THE // // USER TELLING THE DYNAMICALLY LINKED VERSION IS TOO RECENT AND NOT // // COMPATIBLE WITH THIS SOFTWARE. THE MESSAGE MUST INVITE THE USER // // TO UPGRADE HIS SOFTWARE WITH A MORE RECENT VERSION COMPATIBLE WITH // // THIS LIBDAR RELEASE. // //////////////////////////////////////////////////////////////////////// /// return the libdar version, and make libdar initialization (may throw Exceptions) /// It is mandatory to call this function (or another one of the get_version* family) /// \param[out] major the major number of the version /// \param[out] medium the medium number of the version /// \param[out] minor the minor number of the version /// \param[in] init_libgcrypt whether to initialize libgcrypt if not already done (not used if libcrypt is not linked with libdar) /// \note the calling application should check that the major function /// is the same as the libdar used at compilation time. See API tutorial for a /// sample code. extern void get_version(U_I & major, U_I & medium, U_I & minor, bool init_libgcrypt = true); /// returns the libdar version and make libdar initialization (may throw Exceptions) /// \param[out] major the major number of the version /// \param[out] medium the medium number of the version /// \param[out] minor the minor number of the version /// \param[in] init_libgcrypt whether to initialize libgcrypt if not already done (not used if libcrypt is not linked with libdar) /// \param[in] init_gpgme whether to initialize gpgme (not used if gpgme is not linked with libdar) /// \note at the difference of the previous version this one allow the caller to /// avoid initializing gpgme. extern void get_version(U_I & major, U_I & medium, U_I & minor, bool init_libgcrypt, bool init_gpgme); /// this method is to be used when you don't want to bother with major, medium and minor extern void get_version(bool init_libgcrypt = true); /////////////////////////////////////////////// // CLOSING/CLEANING LIBDAR // /////////////////////////////////////////////// // while libdar has only a single boolean as global variable // that defines whether the library is initialized or not // it must proceed to mutex, and dependent libraries initializations // (liblzo, libgcrypt, etc.), which is done during the get_version() call // Some library also need to clear some data so the following call // is provided in that aim and must be called when libdar will no more // be used by the application. extern void close_and_clean(); /////////////////////////////////////////////// // THREAD CANCELLATION ROUTINES // /////////////////////////////////////////////// #if MUTEX_WORKS /// thread cancellation activation /// ask that any libdar code running in the thread given as argument be cleanly aborted /// when the execution will reach the next libdar checkpoint /// \param[in] tid is the Thread ID to cancel libdar in /// \param[in] immediate whether to cancel thread immediately or just signal the request to the thread /// \param[in] flag an arbitrary value passed as-is through libdar extern void cancel_thread(pthread_t tid, bool immediate = true, U_64 flag = 0); /// consultation of the cancellation status of a given thread /// \param[in] tid is the tid of the thread to get status about /// \return false if no cancellation has been requested for the given thread extern bool cancel_status(pthread_t tid); /// thread cancellation deactivation /// abort the thread cancellation for the given thread /// \return false if no thread cancellation was under process for that thread /// or if there is no more pending cancellation (thread has already been canceled). extern bool cancel_clear(pthread_t tid); /// gives the number of thread running libdar extern U_I get_thread_count(); #endif /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_tube.hpp0000644000175000017500000000545314636066467013651 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_tube.hpp /// \brief class to record named pipes in a catalogue /// \ingroup Private #ifndef CAT_TUBE_HPP #define CAT_TUBE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_inode.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the named pipe class class cat_tube : public cat_inode { public : cat_tube(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & xname, const infinint & fs_device): cat_inode(xuid, xgid, xperm, last_access, last_modif, last_change, xname, fs_device) { set_saved_status(saved_status::saved); }; cat_tube(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small): cat_inode(dialog, pdesc, reading_ver, saved, small) {}; cat_tube(const cat_tube & ref) = default; cat_tube(cat_tube && ref) noexcept = default; cat_tube & operator = (const cat_tube & ref) = default; cat_tube & operator = (cat_tube && ref) = default; ~cat_tube() = default; virtual bool operator == (const cat_entree & ref) const override; // using dump from cat_inode class // using method is_more_recent_than() from cat_inode class // using method has_changed_since() from cat_inode class /// inherited from cat_entree virtual unsigned char signature() const override { return 'p'; }; virtual std::string get_description() const override { return "named pipe"; }; /// inherited from cat_entree virtual cat_entree *clone() const override { return new (std::nothrow) cat_tube(*this); }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mycurl_slist.cpp0000644000175000017500000000460014636066467014600 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "mycurl_slist.hpp" #include "erreurs.hpp" #include "tools.hpp" using namespace std; namespace libdar { #if LIBCURL_AVAILABLE bool mycurl_slist::operator == (const mycurl_slist & ref) const { if(appended.size() != ref.appended.size()) return false; else { deque::const_iterator itref = ref.appended.begin(); deque::const_iterator itme = appended.begin(); while(itref != ref.appended.end() && itme != appended.end() && *itref == *itme) { ++itref; ++itme; } return itref == ref.appended.end() && itme == appended.end(); } } void mycurl_slist::append(const string & s) { curl_slist *tmp = curl_slist_append(header, s.c_str()); if(tmp == nullptr) throw Erange("mycurl_slist::append", "Failed to append command to a curl_slist"); header = tmp; appended.push_back(s); } curl_slist* mycurl_slist::rebuild(const deque & ap) { curl_slist *ret = nullptr; curl_slist *tmp = nullptr; deque::const_iterator it = ap.begin(); try { while(it != ap.end()) { tmp = curl_slist_append(ret, it->c_str()); if(tmp == nullptr) throw Erange("mycurl_slist::rebuild", "Failed to rebuild an slist from its recorded paramaters"); ret = tmp; ++it; } } catch(...) { release(ret); throw; } return ret; } #endif } // end of namespace dar-2.7.15/src/libdar/database_header.hpp0000644000175000017500000000555114636066467015136 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file database_header.hpp /// \brief defines the database structure in file /// \ingroup Private #ifndef DATABASE_HEADER_HPP #define DATABASE_HEADER_HPP #include "../my_config.h" #include "generic_file.hpp" #include "user_interaction.hpp" #include "compression.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// create the header for a dar_manager database /// \param[in] dialog is used for user interaction /// \param[in] filename is the file's name to create/overwrite /// \param[in] overwrite set to true to allow file overwriting (else generates an error if file exists) /// \param[in] algozip compression algorithm used for the database /// \param[in] compr_level compression level /// \return the database header has been read and checked the database can now be read from the returned generic_file pointed by the returned value /// then it must be destroyed with the delete operator. extern generic_file *database_header_create(const std::shared_ptr & dialog, const std::string & filename, bool overwrite, compression algozip, U_I compr_level); /// read the header of a dar_manager database /// \param[in] dialog for user interaction /// \param[in] filename is the filename to read from /// \param[out] db_version version of the database /// \param[out] algozip compression algorithm used in the database /// \param[out] compr_level compression level used in the database /// \return the generic_file where the database header has been put extern generic_file *database_header_open(const std::shared_ptr & dialog, const std::string & filename, unsigned char & db_version, compression & algozip, U_I & compr_level); extern const unsigned char database_header_get_supported_version(); ///@} } // end of namespace #endif dar-2.7.15/src/libdar/cat_signature.hpp0000644000175000017500000001174114636066467014710 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_signature.hpp /// \brief class used to write and read from archive the type and status of each entry in a catalogue /// \ingroup Private #ifndef CAT_SIGNATURE_HPP #define CAT_SIGNATURE_HPP #include "../my_config.h" #include "generic_file.hpp" #include "archive_version.hpp" #include "cat_status.hpp" extern "C" { } // end extern "C" namespace libdar { /// \addtogroup Private /// @{ /// class cat_signature combines the cat_entree::signature() of the object with its saved_status and read and store this combinason /// historically these two fields were saved on a single byte, but as libdar received new feature it was to narrow /// where from this class to transparently manage this field evolution class cat_signature { public: /// set a signature from running libdar cat_signature(unsigned char original, saved_status status); // -> mk_signature() /// set a signature from an disk archive cat_signature(generic_file & f, const archive_version & reading_ver); /// read a signature from archive for an existing cat_signature object (overwrite its value) /// \param[in] f where to read data from /// \param[in] reading_ver which format to expect /// \return true if data could be read, false else. /// \note the validity of the read data is not checked at that time /// but a the time get_base_and_status() is called bool read(generic_file & f, const archive_version & reading_ver); void write(generic_file &f); /// provide typ and status as read from the archive /// \param[out] base the signature() of the entry /// \param[out] saved the get_saved_status() of the entry /// \return false if the field read from the archive is malformed, in which case the returned argument are meaningless bool get_base_and_status(unsigned char & base, saved_status & saved) const; static bool compatible_signature(unsigned char a, unsigned char b); private: static constexpr U_8 SAVED_FAKE_BIT = 0x80; static constexpr U_8 SAVED_NON_DELTA_BIT = 0x40; /// stores file type and status information /// \note this field "field" stores two types of information: //. type of inode: and the nature of the object that has to be build from the following bytes //. status of the info: on which depends whether some or all field should be read in the following data /// /// Historically, the type was stored as different letters, then the differential backup /// has been added and the saved/not_saved status appeared as /// uppercase for not_saved status and lowercase for saved status (backward compatibility) /// /// adding features after features, the FAKE status used in isolated catalogues used /// the bit 8 (ASCII used 7 lower bits only). /// /// but while adding the delta-diff feature it has been realized that 3 bits could be used beside /// information type to store the status thanks to the way ASCI encodes letters: //. lower case letters have binary values 011xxxxx //. and higher case have binary values 010xxxxx /// the 5 lower bits encode the letter nature, remains the bits 6, 7 and 8 to encode /// the status of the inode: //. 011----- (3) status is "saved" (backward compatibility) this is a lowercase letter //. 010----- (2) status is "not_saved" (backward compatibily) this is an uppercase letter //. 111----- (7) status is "fake" (backward compatibility) used only with lowercase letters //. 001----- (1) status is "delta" (setting back the byte 6 to 1 gives the letter value) //. 100----- (4) status is "inode_only (setting back the bytes as 011 gives the letter value) //. 101----- (5) status is unused (setting back the high bytes to 011 gives the letter value) //. 110----- (6) status is unused (setting back the high bytes to 011 gives the letter value) //. 000----- (0) status is unused (setting back the high bytes to 011 gives the letter value) unsigned char field; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_inode.cpp0000644000175000017500000011502314636310205013754 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #ifdef STDC_HEADERS #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" #include "cat_inode.hpp" #include "cat_lien.hpp" #include "tools.hpp" #include "cat_signature.hpp" /// all values of FLAG for EA are covered by the EA_MASK: #define INODE_FLAG_EA_MASK 0x07 // FLAG values for EA #define INODE_FLAG_EA_FULL 0x01 #define INODE_FLAG_EA_PART 0x02 #define INODE_FLAG_EA_NONE 0x03 #define INODE_FLAG_EA_FAKE 0x04 #define INODE_FLAG_EA_REMO 0x05 /// all values of FLAG for FSA are covered by the FSA_MASK: #define INODE_FLAG_FSA_MASK 0x18 // FLAG values for FSA #define INODE_FLAG_FSA_NONE 0x00 #define INODE_FLAG_FSA_PART 0x08 #define INODE_FLAG_FSA_FULL 0x10 using namespace std; namespace libdar { const ea_attributs cat_inode::empty_ea; cat_inode::cat_inode(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const string & xname, const infinint & fs_device) : cat_nomme(xname, saved_status::saved) { nullifyptr(); uid = xuid; gid = xgid; perm = xperm; ea_saved = ea_saved_status::none; fsa_saved = fsa_saved_status::none; edit = 0; small_read = false; try { last_acc = last_access; last_mod = last_modif; last_cha = last_change; fs_dev = new (nothrow) infinint(fs_device); if(fs_dev == nullptr) throw Ememory("cat_inode::cat_inode"); } catch(...) { destroy(); throw; } } cat_inode::cat_inode(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small) : cat_nomme(pdesc, small, saved) { U_16 tmp; unsigned char flag; generic_file *ptr = nullptr; pdesc->check(small); if(small) ptr = pdesc->esc; else ptr = pdesc->stack; nullifyptr(); try { edit = reading_ver; small_read = small; if(reading_ver > 1) { ptr->read((char *)(&flag), 1); unsigned char ea_flag = flag & INODE_FLAG_EA_MASK; switch(ea_flag) { case INODE_FLAG_EA_FULL: ea_saved = ea_saved_status::full; break; case INODE_FLAG_EA_PART: ea_saved = ea_saved_status::partial; break; case INODE_FLAG_EA_NONE: ea_saved = ea_saved_status::none; break; case INODE_FLAG_EA_FAKE: ea_saved = ea_saved_status::fake; break; case INODE_FLAG_EA_REMO: ea_saved = ea_saved_status::removed; break; default: throw Erange("cat_inode::cat_inode", gettext("badly structured inode: unknown inode flag")); } } else ea_saved = ea_saved_status::none; if(reading_ver <= 7) { // UID and GID were stored on 16 bits each if(ptr->read((char *)&tmp, sizeof(tmp)) != sizeof(tmp)) throw Erange("cat_inode::cat_inode", gettext("missing data to build an inode")); uid = ntohs(tmp); if(ptr->read((char *)&tmp, sizeof(tmp)) != sizeof(tmp)) throw Erange("cat_inode::cat_inode", gettext("missing data to build an inode")); gid = ntohs(tmp); } else // archive format >= "08" { uid = infinint(*ptr); gid = infinint(*ptr); } if(ptr->read((char *)&tmp, sizeof(tmp)) != sizeof(tmp)) throw Erange("cat_inode::cat_inode", gettext("missing data to build an inode")); perm = ntohs(tmp); last_acc.read(*ptr, reading_ver); last_mod.read(*ptr, reading_ver); if(reading_ver >= 8) { last_cha.read(*ptr, reading_ver); if(ea_saved == ea_saved_status::full) { ea_size = new (nothrow) infinint(*ptr); if(ea_size == nullptr) throw Ememory("cat_inode::cat_inode(file)"); } } else // archive format <= 7 { // ea_size stays nullptr meaning EA size unknown (old format) last_cha.nullify(); } if(!small) // reading a full entry from catalogue { switch(ea_saved) { case ea_saved_status::full: ea_offset = new (nothrow) infinint(*ptr); if(ea_offset == nullptr) throw Ememory("cat_inode::cat_inode(file)"); if(reading_ver <= 7) { ea_crc = create_crc_from_file(*ptr, true); if(ea_crc == nullptr) throw SRC_BUG; last_cha.read(*ptr, reading_ver); } else // archive format >= 8 { ea_crc = create_crc_from_file(*ptr, false); if(ea_crc == nullptr) throw SRC_BUG; } break; case ea_saved_status::partial: case ea_saved_status::fake: if(reading_ver <= 7) { last_cha.read(*ptr, reading_ver); } break; case ea_saved_status::none: case ea_saved_status::removed: if(reading_ver <= 7) { last_cha.nullify(); } break; default: throw SRC_BUG; } } else // reading a small dump using escape sequence marks { // header version is greater than or equal to "08" (small dump appeared at // this version of archive format) ea_offset is not used (sequential read mode) // while ea_CRC has been dumped a bit further in that case, we will fetch // its value upon request by get_ea() or ea_get_crc() // methods } ea = nullptr; // in any case if(reading_ver >= 9) { unsigned char fsa_flag = flag & INODE_FLAG_FSA_MASK; switch(fsa_flag) { case INODE_FLAG_FSA_NONE: fsa_saved = fsa_saved_status::none; break; case INODE_FLAG_FSA_PART: fsa_saved = fsa_saved_status::partial; break; case INODE_FLAG_FSA_FULL: fsa_saved = fsa_saved_status::full; break; default: throw Erange("cat_inode::cat_inode", gettext("badly structured inode: unknown inode flag for FSA")); } if(fsa_saved != fsa_saved_status::none) { fsa_families = new (nothrow) infinint(*ptr); if(fsa_families == nullptr) throw Ememory("cat_inode::cat_inode(file)"); } if(fsa_saved == fsa_saved_status::full) { fsa_size = new (nothrow) infinint(*ptr); if(fsa_size == nullptr) throw Ememory("cat_inode::cat_inode(file)"); } if(!small) { switch(fsa_saved) { case fsa_saved_status::full: fsa_offset = new (nothrow) infinint(*ptr); fsa_crc = create_crc_from_file(*ptr); if(fsa_offset == nullptr || fsa_crc == nullptr) throw Ememory("cat_inode::cat_inode(file)"); break; case fsa_saved_status::partial: case fsa_saved_status::none: break; default: throw SRC_BUG; } } else // reading a small dump using escape sequence marks { // fsa_offset is not used and fsa_CRC have been dumped a bit // further in that case (sequential read mode), // and will be fetched by get_fsa() or ea_get_crc() // methods } } else // older archive than version 9 do not support FSA fsa_saved = fsa_saved_status::none; } catch(...) { destroy(); throw; } } cat_inode::cat_inode(const cat_inode & ref) : cat_nomme(ref) { nullifyptr(); try { copy_from(ref); } catch(...) { destroy(); throw; } } cat_inode & cat_inode::operator = (const cat_inode & ref) { cat_nomme *me = this; const cat_nomme *nref = &ref; *me = *nref; // copying the "cat_nomme" part of the object destroy(); copy_from(ref); return *this; } cat_inode::~cat_inode() noexcept(false) { destroy(); } bool cat_inode::same_as(const cat_inode & ref) const { return cat_nomme::same_as(ref) && cat_signature::compatible_signature(signature(), ref.signature()); } bool cat_inode::is_more_recent_than(const cat_inode & ref, const infinint & hourshift) const { return ref.last_mod < last_mod && !tools_is_equal_with_hourshift(hourshift, ref.last_mod, last_mod); } bool cat_inode::has_changed_since(const cat_inode & ref, const infinint & hourshift, comparison_fields what_to_check) const { return (what_to_check != comparison_fields::inode_type && (!hourshift.is_zero() ? !tools_is_equal_with_hourshift(hourshift, ref.last_mod, last_mod) : !ref.last_mod.loose_equal(last_mod))) || (what_to_check == comparison_fields::all && uid != ref.uid) || (what_to_check == comparison_fields::all && gid != ref.gid) || (what_to_check != comparison_fields::mtime && what_to_check != comparison_fields::inode_type && perm != ref.perm); } void cat_inode::compare(const cat_inode &other, const mask & ea_mask, comparison_fields what_to_check, const infinint & hourshift, bool symlink_date, const fsa_scope & scope, bool isolated_mode) const { bool do_mtime_test = dynamic_cast(&other) == nullptr || symlink_date; if(!same_as(other)) throw Erange("cat_inode::compare",gettext("different file type")); if(what_to_check == comparison_fields::all && get_uid() != other.get_uid()) { infinint u1 = get_uid(); infinint u2 = other.get_uid(); throw Erange("cat_inode.compare", tools_printf(gettext("different owner (uid): %i <--> %i"), &u1, &u2)); } if(what_to_check == comparison_fields::all && get_gid() != other.get_gid()) { infinint g1 = get_gid(); infinint g2 = other.get_gid(); throw Erange("cat_inode.compare", tools_printf(gettext("different owner group (gid): %i <--> %i"), &g1, &g2)); } if((what_to_check == comparison_fields::all || what_to_check == comparison_fields::ignore_owner) && get_perm() != other.get_perm()) { string p1 = tools_int2octal(get_perm()); string p2 = tools_int2octal(other.get_perm()); throw Erange("cat_inode.compare", tools_printf(gettext("different permission: %S <--> %S"), &p1, &p2)); } if(do_mtime_test && (what_to_check == comparison_fields::all || what_to_check == comparison_fields::ignore_owner || what_to_check == comparison_fields::mtime) && !tools_is_equal_with_hourshift(hourshift, get_last_modif(), other.get_last_modif())) { string s1 = tools_display_date(get_last_modif()); string s2 = tools_display_date(other.get_last_modif()); throw Erange("cat_inode.compare", tools_printf(gettext("difference of last modification date: %S <--> %S"), &s1, &s2)); } sub_compare(other, isolated_mode); switch(ea_get_saved_status()) { case ea_saved_status::full: if(other.ea_get_saved_status() == ea_saved_status::full) { if(!isolated_mode) { const ea_attributs *me = get_ea(); // this pointer must not be freed const ea_attributs *you = other.get_ea(); // this pointer must not be freed neither if(me->diff(*you, ea_mask)) throw Erange("cat_inode::compare", gettext("different Extended Attributes")); } } else { #ifdef EA_SUPPORT throw Erange("cat_inode::compare", gettext("no Extended Attribute to compare with")); #else throw Ecompilation(gettext("Cannot compare EA: EA support has not been activated at compilation time")); #endif } // else we ignore the EA present in the argument, // this is not a symetrical comparison // we check that all data in current object are the same in the argument // but additional data can reside in the argument break; case ea_saved_status::partial: case ea_saved_status::fake: if(other.ea_get_saved_status() != ea_saved_status::none && other.ea_get_saved_status() != ea_saved_status::removed) { if(!tools_is_equal_with_hourshift(hourshift, get_last_change(), other.get_last_change()) && get_last_change() < other.get_last_change()) throw Erange("cat_inode::compare", gettext("inode last change date (ctime) greater, EA might be different")); } else { #ifdef EA_SUPPORT throw Erange("cat_inode::compare", gettext("no Extended Attributes to compare with")); #else throw Ecompilation(gettext("Cannot compare EA: EA support has not been activated at compilation time")); #endif } break; case ea_saved_status::none: case ea_saved_status::removed: break; default: throw SRC_BUG; } switch(fsa_get_saved_status()) { case fsa_saved_status::full: if(other.fsa_get_saved_status() == fsa_saved_status::full) { if(!isolated_mode) { const filesystem_specific_attribute_list *me = get_fsa(); const filesystem_specific_attribute_list *you = other.get_fsa(); if(me == nullptr) throw SRC_BUG; if(you == nullptr) throw SRC_BUG; if(!me->is_included_in(*you, scope)) throw Erange("cat_inode::compare", gettext("different Filesystem Specific Attributes")); } } else { if(scope.size() > 0) throw Erange("cat_inode::compare", gettext("No Filesystem Specific Attribute to compare with")); // this is not perfect, the "other" could have no FSA due to the non empty scope excluding only // the valid FSAs. A more specific comparison would worth it... } break; case fsa_saved_status::partial: if(other.fsa_get_saved_status() != fsa_saved_status::none) { if(!tools_is_equal_with_hourshift(hourshift, get_last_change(), other.get_last_change()) && get_last_change() < other.get_last_change()) throw Erange("cat_inode::compare", gettext("inode last change date (ctime) greater, FSA might be different")); } else { if(scope.size() > 0) throw Erange("cat_inode::compare", gettext("Filesystem Specific Attribute are missing")); // this is not perfect, the "other" could have no FSA due to the non empty scope excluding only // the valid FSAs. A more specific comparison would worth it... } break; case fsa_saved_status::none: break; // nothing to check default: throw SRC_BUG; } } void cat_inode::inherited_dump(const pile_descriptor & pdesc, bool small) const { U_16 tmp; unsigned char flag = 0; generic_file *ptr = nullptr; pdesc.check(small); if(small) ptr = pdesc.esc; else ptr = pdesc.stack; // setting up the flag field switch(ea_saved) { case ea_saved_status::none: flag |= INODE_FLAG_EA_NONE; break; case ea_saved_status::partial: flag |= INODE_FLAG_EA_PART; break; case ea_saved_status::fake: flag |= INODE_FLAG_EA_FAKE; break; case ea_saved_status::full: flag |= INODE_FLAG_EA_FULL; break; case ea_saved_status::removed: flag |= INODE_FLAG_EA_REMO; break; default: throw SRC_BUG; // unknown value for ea_saved } switch(fsa_saved) { case fsa_saved_status::none: flag |= INODE_FLAG_FSA_NONE; break; case fsa_saved_status::partial: flag |= INODE_FLAG_FSA_PART; break; case fsa_saved_status::full: flag |= INODE_FLAG_FSA_FULL; break; default: throw SRC_BUG; // unknown value for fsa_saved } // saving parent class data cat_nomme::inherited_dump(pdesc, small); // saving unix inode specific part ptr->write((char *)(&flag), 1); uid.dump(*ptr); gid.dump(*ptr); tmp = htons(perm); ptr->write((char *)&tmp, sizeof(tmp)); last_acc.dump(*ptr); last_mod.dump(*ptr); last_cha.dump(*ptr); // EA part if(ea_saved == ea_saved_status::full) ea_get_size().dump(*ptr); if(!small) { switch(ea_saved) { case ea_saved_status::full: if(ea_offset == nullptr) throw SRC_BUG; ea_offset->dump(*ptr); if(ea_crc == nullptr) throw SRC_BUG; ea_crc->dump(*ptr); break; case ea_saved_status::partial: case ea_saved_status::fake: case ea_saved_status::none: case ea_saved_status::removed: break; default: throw SRC_BUG; } } // FSA part if(fsa_saved != fsa_saved_status::none) { if(fsa_families == nullptr) throw SRC_BUG; fsa_families->dump(*ptr); } if(fsa_saved == fsa_saved_status::full) { if(fsa_size == nullptr) throw SRC_BUG; fsa_size->dump(*ptr); } if(!small) { switch(fsa_saved) { case fsa_saved_status::full: if(fsa_offset == nullptr) throw SRC_BUG; fsa_offset->dump(*ptr); if(fsa_crc == nullptr) throw SRC_BUG; fsa_crc->dump(*ptr); break; case fsa_saved_status::partial: case fsa_saved_status::none: break; default: throw SRC_BUG; } } } void cat_inode::ea_set_saved_status(ea_saved_status status) { if(status == ea_saved) return; switch(status) { case ea_saved_status::none: case ea_saved_status::removed: case ea_saved_status::partial: case ea_saved_status::fake: if(ea != nullptr) { delete ea; ea = nullptr; } if(ea_offset != nullptr) { delete ea_offset; ea_offset = nullptr; } break; case ea_saved_status::full: if(ea != nullptr) throw SRC_BUG; if(ea_offset != nullptr) throw SRC_BUG; break; default: throw SRC_BUG; } ea_saved = status; } void cat_inode::ea_attach(ea_attributs *ref) { if(ea_saved != ea_saved_status::full) throw SRC_BUG; if(ref != nullptr && ea == nullptr) { if(ea_size != nullptr) { delete ea_size; ea_size = nullptr; } ea_size = new (nothrow) infinint(ref->space_used()); if(ea_size == nullptr) throw Ememory("cat_inode::ea_attach"); ea = ref; } else throw SRC_BUG; } const ea_attributs *cat_inode::get_ea() const { switch(ea_saved) { case ea_saved_status::full: if(ea != nullptr) return ea; else if(get_pile() != nullptr) // reading from archive { crc *val = nullptr; const crc *my_crc = nullptr; try { if(!small_read) // direct read mode { if(ea_offset == nullptr) throw SRC_BUG; get_pile()->flush_read_above(get_compressor_layer()); get_compressor_layer()->resume_compression(); get_pile()->skip(*ea_offset); } else // sequential read mode { if(get_escape_layer() == nullptr) throw SRC_BUG; // passing over the delta signature mark if any (void)get_escape_layer()->skip_to_next_mark(escape::seqt_delta_sig, false); // warning this section calls *esc directly while it may be managed by another thread // we are reading from the stack the possible thread is not in read_ahead operation // so it is pending for read request or other orders if(!get_escape_layer()->skip_to_next_mark(escape::seqt_ea, false)) throw Erange("cat_inode::get_ea", string("Error while fetching EA from archive: No escape mark found for that file")); // resuming compression (EA are always stored compressed) get_pile()->flush_read_above(get_compressor_layer()); get_compressor_layer()->resume_compression(); // we shall reset layers above esc for they do not assume nothing has changed below them get_pile()->flush_read_above(get_escape_layer()); // now we can continue normally using get_pile() const_cast(this)->ea_set_offset(get_pile()->get_position()); } if(ea_get_size().is_zero()) get_pile()->reset_crc(crc::OLD_CRC_SIZE); else { get_pile()->reset_crc(tools_file_size_to_crc_size(ea_get_size())); get_pile()->read_ahead(ea_get_size()); } try { try { if(edit <= 1) throw SRC_BUG; // EA do not exist in that archive format const_cast(ea) = new (nothrow) ea_attributs(*get_pile(), edit); if(ea == nullptr) throw Ememory("cat_inode::get_ea"); } catch(Euser_abort & e) { throw; } catch(Ebug & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { throw Erange("cat_inode::get_ea", string("Error while reading EA from archive: ") + e.get_message()); } } catch(...) { val = get_pile()->get_crc(); // keeps storage in coherent status throw; } val = get_pile()->get_crc(); if(val == nullptr) throw SRC_BUG; ea_get_crc(my_crc); // ea_get_crc() will eventually fetch the CRC for EA from the archive (sequential reading) if(my_crc == nullptr) throw SRC_BUG; if(typeid(*val) != typeid(*my_crc) || *val != *my_crc) throw Erange("cat_inode::get_ea", gettext("CRC error detected while reading EA")); } catch(...) { if(val != nullptr) delete val; throw; } if(val != nullptr) delete val; return ea; } else throw SRC_BUG; // no need of break here throw SRC_BUG; // but ... instead of break we use some more radical precaution. case ea_saved_status::removed: return &empty_ea; // no need of break here default: throw SRC_BUG; } } void cat_inode::ea_detach() const { if(ea != nullptr) { delete ea; const_cast(ea) = nullptr; } } infinint cat_inode::ea_get_size() const { if(ea_saved == ea_saved_status::full) { if(ea_size == nullptr) // reading an old archive { if(ea != nullptr) { const_cast(this)->ea_size = new (nothrow) infinint (ea->space_used()); if(ea_size == nullptr) throw Ememory("cat_inode::ea_get_size"); } else // else we stick with value 0, meaning that we read an old archive return 0; } return *ea_size; } else throw SRC_BUG; } void cat_inode::ea_set_offset(const infinint & pos) { if(ea_offset == nullptr) { ea_offset = new (nothrow) infinint(pos); if(ea_offset == nullptr) throw Ememory("cat_inode::ea_set_offset"); } else *ea_offset = pos; } bool cat_inode::ea_get_offset(infinint & val) const { if(ea_offset != nullptr) { val = *ea_offset; return true; } else return false; } void cat_inode::ea_set_crc(const crc & val) { if(ea_crc != nullptr) { delete ea_crc; ea_crc = nullptr; } ea_crc = val.clone(); if(ea_crc == nullptr) throw Ememory("cat_inode::ea_set_crc"); } void cat_inode::ea_get_crc(const crc * & ptr) const { if(ea_get_saved_status() != ea_saved_status::full) throw SRC_BUG; if(small_read && ea_crc == nullptr) { if(get_escape_layer() == nullptr) throw SRC_BUG; if(get_escape_layer()->skip_to_next_mark(escape::seqt_ea_crc, false)) { crc *tmp = nullptr; try { if(edit >= 8) tmp = create_crc_from_file(*get_escape_layer(), false); else // archive format <= 7 tmp = create_crc_from_file(*get_escape_layer(), true); if(tmp == nullptr) throw SRC_BUG; const_cast(this)->ea_crc = tmp; tmp = nullptr; // the object is now owned by "this" } catch(...) { get_pile()->flush_read_above(get_escape_layer()); if(tmp != nullptr) delete tmp; throw; } get_pile()->flush_read_above(get_escape_layer()); } else // skip failed on the escape layer { crc *tmp = new (nothrow) crc_n(1); // creating a default CRC if(tmp == nullptr) throw Ememory("cat_inode::ea_get_crc"); get_pile()->flush_read_above(get_escape_layer()); try { tmp->clear(); const_cast(this)->ea_crc = tmp; // this is to avoid trying to fetch the CRC a new time if decision // has been taken to continue the operation after the exception // thrown below has been caught. tmp = nullptr; // the object is now owned by "this" } catch(...) { delete tmp; throw; } throw Erange("cat_inode::ea_get_crc", gettext("Error while reading CRC for EA from the archive: No escape mark found for that file")); } } if(ea_crc == nullptr) throw SRC_BUG; else ptr = ea_crc; } bool cat_inode::ea_get_crc_size(infinint & val) const { if(ea_crc != nullptr) { val = ea_crc->get_size(); return true; } else return false; } void cat_inode::fsa_set_saved_status(fsa_saved_status status) { if(status == fsa_saved) return; switch(status) { case fsa_saved_status::none: case fsa_saved_status::partial: if(fsal != nullptr) { delete fsal; fsal = nullptr; } if(fsa_offset != nullptr) { delete fsa_offset; fsa_offset = nullptr; } break; case fsa_saved_status::full: if(fsal != nullptr) throw SRC_BUG; if(fsa_offset != nullptr) throw SRC_BUG; break; default: throw SRC_BUG; } fsa_saved = status; } void cat_inode::fsa_partial_attach(const fsa_scope & val) { if(fsa_saved != fsa_saved_status::partial) throw SRC_BUG; if(fsa_families == nullptr) fsa_families = new(nothrow) infinint(fsa_scope_to_infinint(val)); else *fsa_families = fsa_scope_to_infinint(val); } void cat_inode::fsa_attach(filesystem_specific_attribute_list *ref) { if(fsa_saved != fsa_saved_status::full) throw SRC_BUG; if(ref != nullptr && fsal == nullptr) { if(fsa_size != nullptr) { delete fsa_size; fsa_size = nullptr; } if(fsa_families != nullptr) { delete fsa_families; fsa_families = nullptr; } try { fsa_size = new (nothrow) infinint (ref->storage_size()); fsa_families = new(nothrow) infinint(fsa_scope_to_infinint(ref->get_fsa_families())); if(fsa_size == nullptr || fsa_families == nullptr) throw Ememory("cat_inode::fsa_attach"); } catch(...) { if(fsa_size != nullptr) { delete fsa_size; fsa_size = nullptr; } if(fsa_families != nullptr) { delete fsa_families; fsa_families = nullptr; } throw; } fsal = ref; } else throw SRC_BUG; } void cat_inode::fsa_detach() const { if(fsal != nullptr) { delete fsal; const_cast(this)->fsal = nullptr; } } const filesystem_specific_attribute_list *cat_inode::get_fsa() const { switch(fsa_saved) { case fsa_saved_status::full: if(fsal != nullptr) return fsal; else if(get_pile() != nullptr) // reading from archive { crc *val = nullptr; const crc *my_crc = nullptr; try { generic_file *reader = nullptr; if(get_escape_layer() == nullptr) reader = get_compressor_layer(); else reader = get_escape_layer(); if(reader == nullptr) throw SRC_BUG; // we shall reset layers above reader get_pile()->flush_read_above(reader); if(!small_read) // direct reading mode { if(fsa_offset == nullptr) throw SRC_BUG; reader->skip(*fsa_offset); } else { if(get_escape_layer() == nullptr) throw SRC_BUG; // passing over the delta signature mark if any (void)get_escape_layer()->skip_to_next_mark(escape::seqt_delta_sig, false); // warning this section calls *get_escape_layer() directly while it may be managed by another thread // we are reading from the get_pile() the possible thread is not in read_ahead operation // so it is pending for read request or other orders if(!get_escape_layer()->skip_to_next_mark(escape::seqt_fsa, false)) throw Erange("cat_inode::get_fsa", string("Error while fetching FSA from archive: No escape mark found for that file")); const_cast(this)->fsa_set_offset(get_escape_layer()->get_position()); } if(get_escape_layer() == nullptr) { // FSA is never stored compressed, we must change the compression algo // but only if necessary if(get_compressor_layer()->get_algo() != compression::none) get_compressor_layer()->suspend_compression(); } reader->reset_crc(tools_file_size_to_crc_size(fsa_get_size())); try { try { const_cast(this)->fsal = new (nothrow) filesystem_specific_attribute_list(); if(fsal == nullptr) throw Ememory("cat_inode::get_fsa"); try { reader->read_ahead(fsa_get_size()); const_cast(this)->fsal->read(*reader, edit); } catch(...) { delete fsal; const_cast(this)->fsal = nullptr; throw; } } catch(Euser_abort & e) { throw; } catch(Ebug & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { throw Erange("cat_inode::get_fda", string("Error while reading FSA from archive: ") + e.get_message()); } } catch(...) { val = reader->get_crc(); // keeps storage in coherent status throw; } val = reader->get_crc(); if(val == nullptr) throw SRC_BUG; fsa_get_crc(my_crc); // fsa_get_crc() will eventually fetch the CRC for FSA from the archive (sequential reading) if(my_crc == nullptr) throw SRC_BUG; if(typeid(*val) != typeid(*my_crc) || *val != *my_crc) throw Erange("cat_inode::get_fsa", gettext("CRC error detected while reading FSA")); } catch(...) { if(val != nullptr) delete val; throw; } if(val != nullptr) delete val; return fsal; } else throw SRC_BUG; // no need of break here throw SRC_BUG; // but ... instead of break we use some more radical precaution. default: throw SRC_BUG; } } infinint cat_inode::fsa_get_size() const { if(fsa_saved == fsa_saved_status::full) if(fsa_size != nullptr) return *fsa_size; else throw SRC_BUG; else throw SRC_BUG; } void cat_inode::fsa_set_offset(const infinint & pos) { if(fsa_offset == nullptr) { fsa_offset = new (nothrow) infinint(pos); if(fsa_offset == nullptr) throw Ememory("cat_inode::fsa_set_offset"); } else *fsa_offset = pos; } bool cat_inode::fsa_get_offset(infinint & pos) const { if(fsa_offset != nullptr) { pos = *fsa_offset; return true; } else return false; } void cat_inode::fsa_set_crc(const crc & val) { if(fsa_crc != nullptr) { delete fsa_crc; fsa_crc = nullptr; } fsa_crc = val.clone(); if(fsa_crc == nullptr) throw Ememory("cat_inode::fsa_set_crc"); } void cat_inode::fsa_get_crc(const crc * & ptr) const { if(fsa_get_saved_status() != fsa_saved_status::full) throw SRC_BUG; if(small_read && fsa_crc == nullptr) { if(get_escape_layer() == nullptr) throw SRC_BUG; if(get_pile() == nullptr) throw SRC_BUG; if(get_escape_layer()->skip_to_next_mark(escape::seqt_fsa_crc, false)) { crc *tmp = nullptr; try { tmp = create_crc_from_file(*get_escape_layer(), false); if(tmp == nullptr) throw SRC_BUG; const_cast(this)->fsa_crc = tmp; tmp = nullptr; // the object is now owned by "this" } catch(...) { get_pile()->flush_read_above(get_escape_layer()); if(tmp != nullptr) delete tmp; throw; } get_pile()->flush_read_above(get_escape_layer()); } else // fsa_crc mark not found { crc *tmp = new (nothrow) crc_n(1); // creating a default CRC if(tmp == nullptr) throw Ememory("cat_inode::fsa_get_crc"); get_pile()->flush_read_above(get_escape_layer()); try { tmp->clear(); const_cast(this)->fsa_crc = tmp; // this is to avoid trying to fetch the CRC a new time if decision // has been taken to continue the operation after the exception // thrown below has been caught. tmp = nullptr; // the object is now owned by "this" } catch(...) { delete tmp; throw; } throw Erange("cat_inode::fsa_get_crc", gettext("Error while reading CRC for FSA from the archive: No escape mark found for that file")); } } if(fsa_crc == nullptr) throw SRC_BUG; else ptr = fsa_crc; } bool cat_inode::fsa_get_crc_size(infinint & val) const { if(fsa_crc != nullptr) { val = fsa_crc->get_size(); return true; } else return false; } void cat_inode::nullifyptr() noexcept { ea_offset = nullptr; ea = nullptr; ea_size = nullptr; ea_crc = nullptr; fsa_families = nullptr; fsa_offset = nullptr; fsal = nullptr; fsa_size = nullptr; fsa_crc = nullptr; fs_dev = nullptr; } void cat_inode::destroy() noexcept { if(ea_offset != nullptr) { delete ea_offset; ea_offset = nullptr; } if(ea != nullptr) { delete ea; ea = nullptr; } if(ea_size != nullptr) { delete ea_size; ea_size = nullptr; } if(ea_crc != nullptr) { delete ea_crc; ea_crc = nullptr; } if(fsa_families != nullptr) { delete fsa_families; fsa_families = nullptr; } if(fsa_offset != nullptr) { delete fsa_offset; fsa_offset = nullptr; } if(fsal != nullptr) { delete fsal; fsal = nullptr; } if(fsa_size != nullptr) { delete fsa_size; fsa_size = nullptr; } if(fsa_crc != nullptr) { delete fsa_crc; fsa_crc = nullptr; } if(fs_dev != nullptr) { delete fs_dev; fs_dev = nullptr; } } template void copy_ptr(const T *src, T * & dst) { if(src == nullptr) dst = nullptr; else { dst = new (nothrow) T(*src); if(dst == nullptr) throw Ememory("copy_ptr template"); } } void cat_inode::copy_from(const cat_inode & ref) { try { uid = ref.uid; gid = ref.gid; perm = ref.perm; last_acc = ref.last_acc; last_mod = ref.last_mod; last_cha = ref.last_cha; ea_saved = ref.ea_saved; fsa_saved = ref.fsa_saved; small_read = ref.small_read; copy_ptr(ref.ea_offset, ea_offset); copy_ptr(ref.ea, ea); copy_ptr(ref.ea_size, ea_size); if(ref.ea_crc != nullptr) { ea_crc = (ref.ea_crc)->clone(); if(ea_crc == nullptr) throw Ememory("cat_inode::copy_from"); } else ea_crc = nullptr; copy_ptr(ref.fsa_families, fsa_families); copy_ptr(ref.fsa_offset, fsa_offset); copy_ptr(ref.fsal, fsal); copy_ptr(ref.fsa_size, fsa_size); if(ref.fsa_crc != nullptr) { fsa_crc = (ref.fsa_crc)->clone(); if(fsa_crc == nullptr) throw Ememory("cat_inode::copy_from"); } else fsa_crc = nullptr; copy_ptr(ref.fs_dev, fs_dev); edit = ref.edit; } catch(...) { destroy(); throw; } } void cat_inode::move_from(cat_inode && ref) noexcept { uid = move(ref.uid); gid = move(ref.gid); perm = move(ref.perm); last_acc = move(ref.last_acc); last_mod = move(ref.last_mod); last_cha = move(ref.last_cha); ea_saved = move(ref.ea_saved); fsa_saved = move(ref.fsa_saved); small_read = move(ref.small_read); swap(ref.ea_offset, ea_offset); swap(ref.ea, ea); swap(ref.ea_size, ea_size); swap(ref.ea_crc, ea_crc); swap(ref.fsa_families, fsa_families); swap(ref.fsa_offset, fsa_offset); swap(ref.fsal, fsal); swap(ref.fsa_size, fsa_size); swap(ref.fsa_crc, fsa_crc); swap(ref.fs_dev, fs_dev); edit = move(ref.edit); } } // end of namespace dar-2.7.15/src/libdar/proto_compressor.hpp0000644000175000017500000000466214636066467015503 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file proto_compressor.hpp /// \brief abstracted ancestor class for compressor and parallel_compressor classes /// \ingroup Private #ifndef PROTO_COMPRESSOR_HPP #define PROTO_COMPRESSOR_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" #include "compression.hpp" namespace libdar { /// \addtogroup Private /// @{ class proto_compressor : public generic_file { public : using generic_file::generic_file; proto_compressor(const proto_compressor & ref) = default; proto_compressor(proto_compressor && ref) noexcept = default; proto_compressor & operator = (const proto_compressor & ref) = default; proto_compressor & operator = (proto_compressor && ref) noexcept = default; virtual ~proto_compressor() = default; /// give the compression algo at the current time (must return compression:none if suspended) virtual compression get_algo() const = 0; /// temporary disable compression (reading or writing is just copy to/from the below layer) virtual void suspend_compression() = 0; /// reactivate the compression virtual void resume_compression() = 0; /// whether compression is currently suspended virtual bool is_compression_suspended() const = 0; }; // used only for block compression constexpr const U_I default_uncompressed_block_size = 102400; constexpr const U_I min_uncompressed_block_size = 100; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive.cpp0000644000175000017500000002262514636066467013477 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // extern "C" #include "i_archive.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { // opens an already existing archive archive::archive(const std::shared_ptr & dialog, const path & chem, const string & basename, const string & extension, const archive_options_read & options) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_archive(dialog, chem, basename, extension, options)); if(!pimpl) throw Ememory("archive::archive"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } // creates a new archive archive::archive(const std::shared_ptr & dialog, const path & fs_root, const path & sauv_path, const string & filename, const string & extension, const archive_options_create & options, statistics * progressive_report) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_archive(dialog, fs_root, sauv_path, filename, extension, options, progressive_report)); if(!pimpl) throw Ememory("archive::archive"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } // merge constructor archive::archive(const std::shared_ptr & dialog, const path & sauv_path, shared_ptr ref_arch1, const string & filename, const string & extension, const archive_options_merge & options, statistics * progressive_report) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_archive(dialog, sauv_path, ref_arch1, filename, extension, options, progressive_report)); if(!pimpl) throw Ememory("archive::archive"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } archive::archive(const std::shared_ptr & dialog, const path & chem_src, const string & basename_src, const string & extension_src, const archive_options_read & options_read, const path & chem_dst, const string & basename_dst, const string & extension_dst, const archive_options_repair & options_repair) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_archive(dialog, chem_src, basename_src, extension_src, options_read, chem_dst, basename_dst, extension_dst, options_repair)); if(!pimpl) throw Ememory("archive::archive"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } archive::~archive() = default; statistics archive::op_extract(const path & fs_root, const archive_options_extract & options, statistics * progressive_report) { statistics tmp; NLS_SWAP_IN; try { tmp = pimpl->op_extract(fs_root, options, progressive_report); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } void archive::summary() { NLS_SWAP_IN; try { pimpl->summary(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } archive_summary archive::summary_data() { archive_summary tmp; NLS_SWAP_IN; try { tmp = pimpl->summary_data(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } void archive::op_listing(archive_listing_callback callback, void *context, const archive_options_listing & options) const { NLS_SWAP_IN; try { pimpl->op_listing(callback, context, options); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } statistics archive::op_diff(const path & fs_root, const archive_options_diff & options, statistics * progressive_report) { statistics tmp; NLS_SWAP_IN; try { tmp = pimpl->op_diff(fs_root, options, progressive_report); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } statistics archive::op_test(const archive_options_test & options, statistics * progressive_report) { statistics tmp; NLS_SWAP_IN; try { tmp = pimpl->op_test(options, progressive_report); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } void archive::op_isolate(const path &sauv_path, const string & filename, const string & extension, const archive_options_isolate & options) { NLS_SWAP_IN; try { pimpl->op_isolate(sauv_path, filename, extension, options); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } bool archive::get_children_of(archive_listing_callback callback, void *context, const string & dir, bool fetch_ea) { bool tmp; NLS_SWAP_IN; try { tmp = pimpl->get_children_of(callback, context, dir, fetch_ea); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } const vector archive::get_children_in_table(const string & dir, bool fetch_ea) const { vector tmp; NLS_SWAP_IN; try { tmp = pimpl->get_children_in_table(dir, fetch_ea); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } bool archive::has_subdirectory(const string & dir) const { bool tmp; NLS_SWAP_IN; try { tmp = pimpl->has_subdirectory(dir); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } const entree_stats archive::get_stats() const { entree_stats tmp; NLS_SWAP_IN; try { tmp = pimpl->get_stats(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } const list & archive::get_signatories() const { const list* tmp; NLS_SWAP_IN; try { tmp = &(pimpl->get_signatories()); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return *tmp; } void archive::init_catalogue() const { NLS_SWAP_IN; try { pimpl->init_catalogue(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive::drop_all_filedescriptors() { NLS_SWAP_IN; try { pimpl->drop_all_filedescriptors(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive::set_to_unsaved_data_and_FSA() { NLS_SWAP_IN; try { pimpl->set_to_unsaved_data_and_FSA(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } U_64 archive::get_first_slice_header_size() const { U_64 tmp; NLS_SWAP_IN; try { tmp = pimpl->get_first_slice_header_size(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } U_64 archive::get_non_first_slice_header_size() const { U_64 tmp; NLS_SWAP_IN; try { tmp = pimpl->get_non_first_slice_header_size(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return tmp; } } // end of namespace dar-2.7.15/src/libdar/statistics.cpp0000644000175000017500000001124214636066467014241 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif } #include #include "statistics.hpp" #include "tools.hpp" using namespace std; namespace libdar { void statistics::clear() { if(locking) { LOCK_IN; treated = hard_links = skipped = inode_only = ignored = tooold = errored = deleted = ea_treated = byte_amount = fsa_treated = 0; LOCK_OUT; } else treated = hard_links = skipped = inode_only = ignored = tooold = errored = deleted = ea_treated = byte_amount = fsa_treated = 0; } infinint statistics::total() const { infinint ret; if(locking) { LOCK_IN_CONST; ret = treated+skipped+inode_only+ignored+tooold+errored+deleted; // hard_link are also counted in other counters LOCK_OUT_CONST; } else ret = treated+skipped+inode_only+ignored+tooold+errored+deleted; return ret; } void statistics::init(bool lock) { locking = lock; #if MUTEX_WORKS if(locking) if(pthread_mutex_init(&lock_mutex, nullptr) < 0) throw Erange("statistics::statistics", string(dar_gettext("Error while initializing \"mutex\" for class \"statistics\": ")) + tools_strerror_r(errno)); #else if(locking) throw Ecompilation("Thread support not activated, cannot use statistics object with lock activated"); #endif if(locking) { increment = & statistics::increment_locked; add_to = & statistics::add_to_locked; returned = & statistics::returned_locked; decrement = & statistics::decrement_locked; set_to = & statistics::set_to_locked; sub_from = & statistics::sub_from_locked; } else { increment = & statistics::increment_unlocked; add_to = & statistics::add_to_unlocked; returned = & statistics::returned_unlocked; decrement = & statistics::decrement_unlocked; set_to = & statistics::set_to_unlocked; sub_from = & statistics::sub_from_unlocked; } } void statistics::detruit() { #if MUTEX_WORKS if(locking) pthread_mutex_destroy(&lock_mutex); #endif } void statistics::copy_from(const statistics & ref) { init(ref.locking); treated = ref.treated; hard_links = ref.hard_links; skipped = ref.skipped; inode_only = ref.inode_only; ignored = ref.ignored; tooold = ref.tooold; errored = ref.errored; deleted = ref.deleted; ea_treated = ref.ea_treated; byte_amount = ref.byte_amount; fsa_treated = ref.fsa_treated; } void statistics::move_from(statistics && ref) noexcept { #if MUTEX_WORKS swap(lock_mutex, ref.lock_mutex); #endif swap(locking, ref.locking); treated = move(ref.treated); hard_links = move(ref.hard_links); skipped = move(ref.skipped); inode_only = move(ref.inode_only); ignored = move(ref.ignored); tooold = move(ref.tooold); errored = move(ref.errored); deleted = move(ref.deleted); ea_treated = move(ref.ea_treated); byte_amount = move(ref.byte_amount); fsa_treated = move(ref.fsa_treated); } void statistics::dump(user_interaction & dialog) const { dialog.printf("--------- Statistics DUMP ----------"); dialog.printf("locking = %c", locking ? 'y' : 'n'); dialog.printf("treated = %i", &treated); dialog.printf("hard_links = %i", &hard_links); dialog.printf("skipped = %i", &skipped); dialog.printf("inode only = %i", &inode_only); dialog.printf("ignored = %i", &ignored); dialog.printf("tooold = %i", &tooold); dialog.printf("errored = %i", &errored); dialog.printf("deleted = %i", &deleted); dialog.printf("ea_treated = %i", &ea_treated); dialog.printf("byte_amount = %i", &byte_amount); dialog.printf("fsa_treated = %i", &fsa_treated); dialog.printf("------------------------------------"); } } // end of namespace dar-2.7.15/src/libdar/zstd_module.cpp0000644000175000017500000000734214636066467014406 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_ZSTD_H #include #endif } #include "zstd_module.hpp" #include "int_tools.hpp" #include "tools.hpp" using namespace std; namespace libdar { zstd_module::zstd_module(U_I compression_level) { #if LIBZSTD_AVAILABLE if(compression_level > (U_I)ZSTD_maxCLevel() || compression_level < 1) throw Erange("zstd_module::zstd_module", tools_printf(gettext("out of range ZSTD compression level: %d"), compression_level)); level = compression_level; #else throw Ecompilation(gettext("zstd compression")); #endif } U_I zstd_module::get_max_compressing_size() const { #if LIBZSTD_AVAILABLE U_I unused = 0; return int_tools_maxof_aggregate(unused); #else throw Ecompilation(gettext("zstd compression")); #endif } U_I zstd_module::get_min_size_to_compress(U_I clear_size) const { #if LIBZSTD_AVAILABLE if(clear_size > get_max_compressing_size() || clear_size < 1) throw Erange("zstd_module::get_min_size_to_compress", gettext("out of range block size submitted to zstd_module::get_min_size_to_compress")); return ZSTD_compressBound(clear_size); #else throw Ecompilation(gettext("zstd compression")); #endif } U_I zstd_module::compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const { #if LIBZSTD_AVAILABLE size_t ret; if(normal_size > get_max_compressing_size()) throw Erange("zstd_module::compress_data", "oversized uncompressed data given to ZSTD compression engine"); ret = ZSTD_compress(zip_buf, zip_buf_size, normal, normal_size, level); if(ZSTD_isError(ret)) throw Erange("zstd_module::uncompress_data", tools_printf(gettext("libzstd returned an error while performing block compression: %s"), ZSTD_getErrorName(ret))); return (U_I)ret; #else throw Ecompilation(gettext("zstd compression")); #endif } U_I zstd_module::uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const { #if LIBZSTD_AVAILABLE size_t ret = ZSTD_decompress(normal, normal_size, zip_buf, zip_buf_size); if(ZSTD_isError(ret)) throw Erange("zstd_module::uncompress_data", tools_printf(gettext("libzstd returned an error while performing block decompression: %s"), ZSTD_getErrorName(ret))); return (U_I)ret; #else throw Ecompilation(gettext("zstd compression")); #endif } unique_ptr zstd_module::clone() const { #if LIBZSTD_AVAILABLE try { return std::make_unique(*this); } catch(bad_alloc &) { throw Ememory("zstd_module::clone"); } #else throw Ecompilation(gettext("zstd compression")); #endif } } // end of namespace dar-2.7.15/src/libdar/block_compressor.hpp0000644000175000017500000001023614636066467015424 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file block_compressor.hpp /// \brief provide per block compression/decompression independant from libthreadar but single threaded /// \ingroup Private #ifndef BLOCK_COMPRESSOR_HPP #define BLOCK_COMPRESSOR_HPP #include "../my_config.h" #include "infinint.hpp" #include "crypto_segment.hpp" #include "heap.hpp" #include "compress_module.hpp" #include "proto_compressor.hpp" namespace libdar { /// \addtogroup Private /// @{ class block_compressor: public proto_compressor { public: block_compressor(std::unique_ptr block_zipper, generic_file & compressed_side, U_I uncompressed_bs = default_uncompressed_block_size); // compressed_side is not owned by the object and will remains // after the objet destruction block_compressor(const block_compressor & ref) = delete; block_compressor(block_compressor && ref) noexcept = delete; block_compressor & operator = (const block_compressor & ref) = delete; block_compressor & operator = (block_compressor && ref) noexcept = delete; ~block_compressor(); // inherited from proto_compressor virtual compression get_algo() const override { return suspended? compression::none : zipper->get_algo(); }; virtual void suspend_compression() override; virtual void resume_compression() override; virtual bool is_compression_suspended() const override { return suspended; }; // inherited from generic file virtual bool skippable(skippability direction, const infinint & amount) override; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override; virtual infinint get_position() const override; protected : virtual void inherited_read_ahead(const infinint & amount) override { compressed->read_ahead(amount); }; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override; virtual void inherited_flush_read() override { if(get_mode() != gf_write_only) current->reset(); reof = false; }; virtual void inherited_terminate() override; private: static constexpr const U_I min_uncompressed_block_size = 100; // the local fields std::unique_ptr zipper; ///< compress_module for the requested compression algo generic_file *compressed; ///< where to read from / write to, compressed data U_I uncompressed_block_size; ///< the max block size of un compressed data used bool suspended; ///< whether compression is suspended or not bool need_eof; ///< whether a zero size block need to be added std::unique_ptr current; ///< current block under construction or exploitation bool reof; ///< whether we have hit the end of file while reading // private methods void compress_and_write_current(); void read_and_uncompress_current(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_hard_link_read.hpp0000644000175000017500000001323314636066467017250 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filesystem_hard_link_read.hpp /// \brief filesystem_hard_link_read classes manages hardlinked inode read from filesystem /// \ingroup Private #ifndef FILESYSTEM_HARD_LINK_READ_HPP #define FILESYSTEM_HARD_LINK_READ_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_STAT_H #include #endif } // end extern "C" #include #include "infinint.hpp" #include "fsa_family.hpp" #include "cat_all_entrees.hpp" #include "mem_ui.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// keep trace of hard links when reading the filesystem class filesystem_hard_link_read : protected mem_ui { // this class is not to be used directly // it only provides some routine for the inherited classes public: filesystem_hard_link_read(const std::shared_ptr & dialog, bool x_furtive_read_mode, const fsa_scope & scope) : mem_ui(dialog) { furtive_read_mode = x_furtive_read_mode; sc = scope; ask_before_zeroing_neg_dates = true; }; // the copy of the current object would make copy of addresses in // corres_read that could be released twice ... thus, copy constructor and // assignement are forbidden for this class: filesystem_hard_link_read(const filesystem_hard_link_read & ref) = delete; filesystem_hard_link_read(filesystem_hard_link_read && ref) = delete; filesystem_hard_link_read & operator = (const filesystem_hard_link_read & ref) = delete; filesystem_hard_link_read & operator = (filesystem_hard_link_read && ref) = delete; virtual ~filesystem_hard_link_read() = default; /// get the last assigned number for a hard linked inode const infinint & get_last_etoile_ref() const { return etiquette_counter; }; /// provide the FSA scope used by the object const fsa_scope get_fsa_scope() const { return sc; }; /// don't ask before zeroing negative date just warn user void zeroing_negative_dates_without_asking() { ask_before_zeroing_neg_dates = false; }; /// let the user define which file to not consider as symlinks /// /// \note argument is a list of full paths. No mask or special character is taken into account void set_ignored_symlinks_list(const std::set & x_ignored_symlinks) { ignored_symlinks = x_ignored_symlinks; }; protected: /// reset the whole list of hard linked inodes (hard linked inode stay alive but are no more referenced by the current object) void corres_reset() { corres_read.clear(); etiquette_counter = 0; }; /// create and return a libdar object corresponding to one pointed to by its path /// during this operation, hard linked inode are recorded in a list to be easily pointed /// to by a new reference to it. cat_nomme *make_read_entree(const path & lieu, ///< path of the file to read const std::string & name, ///< name of the file to read bool see_hard_link, ///< whether we want to detect hard_link and eventually return a cat_mirage object (not necessary when diffing an archive with filesystem) const mask & ea_mask ///< which EA to consider when creating the object ); bool get_ask_before_zeroing_neg_dates() const { return ask_before_zeroing_neg_dates; }; private: // private datastructure struct couple { nlink_t count; ///< counts the number of hard link on that inode that have not yet been found in filesystem, once this count reaches zero, the "couple" structure can be dropped and the "holder" too (no more expected hard links to be found) cat_etoile *obj; ///< the address of the corresponding cat_etoile object for that inode cat_mirage holder; ///< it increments by one the obj internal counters, thus, while this object is alive, the obj will not be destroyed couple(cat_etoile *ptr, nlink_t ino_count) : holder("FAKE", ptr) { count = ino_count; obj = ptr; }; }; struct node { node(ino_t num, dev_t dev) { numnode = num; device = dev; }; // this operator is required to use the type node in a std::map bool operator < (const node & ref) const { return numnode < ref.numnode || (numnode == ref.numnode && device < ref.device); }; ino_t numnode; dev_t device; }; // private variable std::map corres_read; infinint etiquette_counter; bool furtive_read_mode; fsa_scope sc; bool ask_before_zeroing_neg_dates; std::set ignored_symlinks; // private methods bool ignore_if_symlink(const std::string & full_path) { return ignored_symlinks.find(full_path) != ignored_symlinks.end(); }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/defile.cpp0000644000175000017500000000452514636066467013305 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "defile.hpp" #include "cat_all_entrees.hpp" using namespace std; namespace libdar { void defile::enfile(const cat_entree *e) { const cat_eod *fin = dynamic_cast(e); const cat_directory *dir = dynamic_cast(e); const cat_nomme *nom = dynamic_cast(e); string s; if(! init) // we must remove previous entry brought by a previous call to this method { if(! chemin.pop(s)) throw SRC_BUG; // no more cat_directory to pop! } else // nothing to be removed init = false; if(fin == nullptr) // not cat_eod { if(nom == nullptr) // not a cat_nomme throw SRC_BUG; // neither cat_eod nor cat_nomme else // a cat_nomme { chemin += nom->get_name(); if(dir != nullptr) init = true; } } cache_set = none; } const string & defile::get_string() const { if(cache_set != full) { cache = chemin.display(); cache_set = full; } return cache; } const string & defile::get_string_without_root() const { if(cache_set != without_root) { cache = chemin.display_without_root(); cache_set = without_root; } return cache; } } // end of namespace dar-2.7.15/src/libdar/thread_cancellation.hpp0000644000175000017500000001734014636066467016044 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file thread_cancellation.hpp /// \brief to be able to cancel libdar operation while running in a given thread. /// \ingroup API /// the class thread_cancellation implemented in this module /// permits both the definition of checkpoints where is looked whether the current /// thread has been marked as to be canceled by the user /// *and* the interface that let the user request a thread to be cancelled /// The advantage of this class is that it then throws a Euser_abort /// exception which properly terminates the libdar operation in the thread /// freeing allocated memory and release mutex properly. Note that the thread /// is not cancelled but the libdar call in this thread returns as soon as a checkpoint /// is met during the execution. #ifndef THREAD_CANCELLATION_HPP #define THREAD_CANCELLATION_HPP #include "../my_config.h" extern "C" { #if MUTEX_WORKS #if HAVE_PTHREAD_H #include #endif #endif } #include #include #include "integers.hpp" #include "erreurs.hpp" namespace libdar { /// \addtogroup Private /// @{ /// class to be used as parent to provide checkpoints to inherited classes /// the class provides a checkpoints to inherited classes and a mechanism /// that let any libdar external code to ask the termination of a libdar /// call executing in a given thread. This does not imply the termination /// of the thread itself but it implies the return of the thread execution /// to the code that called libdar class thread_cancellation { public: /// the constructor thread_cancellation(); /// copy constructor thread_cancellation(const thread_cancellation & ref) = default; /// move constructor thread_cancellation(thread_cancellation && ref) noexcept = default; /// assignment operator thread_cancellation & operator = (const thread_cancellation & ref) = default; /// move operator thread_cancellation & operator = (thread_cancellation && ref) noexcept = default; /// the destructor virtual ~thread_cancellation() noexcept(false); /// Checkpoint test : whether the current libdar call must abort or not /// \exception Euser_abort is thrown if the thread the checkpoint is running /// from is marked as to be canceled. void check_self_cancellation() const; /// by default delayed (non immediate) cancellation generate a specific exception, /// it is possible for delayed cancellation only, do block such exceptions for a certain time ///\param[in] mode can be set to true to block delayed cancellations ///\note when unblocking delayed cancellations, if a delayed cancellation has been ///requested during the ignore time, it will be thrown by this call void block_delayed_cancellation(bool mode); #if MUTEX_WORKS /// marks the thread given in argument as to be canceled /// \param[in] tid the thread ID of the thread where any libdar call must abort /// \param[in] x_immediate whether the cancellation must be as fast as possible or can take a /// \param[in] x_flag is a value to transmit to the Ethread_cancel exception used to cancel libdar's call stack /// little time to make a usable archive static void cancel(pthread_t tid, bool x_immediate, U_64 x_flag); /// gives the cancellation status of the given thread ID /// \param[in] tid the thread to check /// \return true if the given thread is under cancellation static bool cancel_status(pthread_t tid); /// abort the pending thread cancellation /// \return true if the pending thread was still running and /// false if it has already aborted. static bool clear_pending_request(pthread_t tid); /// define association between threads: if a cancellation is requested for src, it will also be requested for dst /// /// \param[in] src orginal target for cancellation /// \param[in] dst additional target for cancellation /// \note that the propagation of cancellation request with association is transitive /// in other word if t1 is associtated with t2 which is associated with t3, /// requesting cancellation of t1 will also lead to signal cancellation request for t2 and t3 /// \note that association is not symmetrical, unless associating A to B *and* B to A static void associate_tid_to_tid(pthread_t src, pthread_t dst); /// remove all association from a given tid to any other thread /// /// \param[in] src is the thread id that has to be removed from the /// association table (see associate_tid_to_tid() above). static void remove_association_for_tid(pthread_t src); /// remove all association for any thread to a given targetted thread /// /// \param[in] dst all association target at dst will be removed static void remove_association_targeted_at(pthread_t dst); /// clean class info from all related information about that thread, like associations and pending cancellations static void dead_thread(pthread_t tid); #endif /// method for debugging/control purposes static U_I count() { #if MUTEX_WORKS return info.size(); #else return 0; #endif }; #if MUTEX_WORKS private: // class types struct fields { pthread_t tid; ///< thread id of the current thread bool block_delayed; ///< whether we buffer any delayed cancellation requests for "this" thread bool immediate; ///< whether we take a few more second to make a real usable archive bool cancellation; ///< true if a thread has to be canceled U_64 flag; ///< user defined informational field, given to the Ethread_cancel constructor }; // object information fields status; // class's static variables and types static pthread_mutex_t access; ///< mutex for the access to "info" static std::list info; ///< list of all object static std::list preborn; ///< canceled thread that still not have a thread_cancellation object to deal with cancellation static std::multimap thread_asso; ///< which other thread to propagate cancellation request to, given a initial tid // helper class routing static void set_cancellation_in_info_for(pthread_t tid, bool cancel_status, bool x_immediate, U_64 x_flag, bool & found, bool & previous_val, bool & bug); static void add_to_preborn(pthread_t tid, bool x_immediate, U_64 x_flag); static void remove_from_preborn(pthread_t tid, bool & found, bool & prev); static void find_asso_tid_with(pthread_t tid, std::multimap::iterator & begin, std::multimap::iterator & end); #endif }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/shell_interaction_emulator.cpp0000644000175000017500000000314114636066467017464 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "shell_interaction_emulator.hpp" extern "C" { } // end extern "C" using namespace std; using namespace libdar; namespace libdar { shell_interaction_emulator::shell_interaction_emulator(user_interaction *emulator): shell_interaction(std::cerr, std::cerr, true) { if(emulator != nullptr) emul = emulator; else throw SRC_BUG; } bool shell_interaction_emulator::inherited_pause(const std::string & message) { bool ret = true; try { emul->pause(message); } catch(Euser_abort & e) { ret = false; } return ret; } } // end of namespace dar-2.7.15/src/libdar/archive_options_listing_shell.cpp0000644000175000017500000000241514636066467020165 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "archive_options_listing_shell.hpp" using namespace std; namespace libdar { void archive_options_listing_shell::clear() { archive_options_listing::clear(); x_list_mode = normal; x_sizes_in_bytes = false; } } // end of namespace dar-2.7.15/src/libdar/mask.hpp0000644000175000017500000004043514636066467013015 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mask.hpp /// \brief here lies a collection of mask classes /// /// The mask classes defined here are to be used to filter files /// in the libdar API calls. /// \ingroup API #ifndef MASK_HPP #define MASK_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_REGEX_H #include #endif } // end extern "C" #include #include #include "erreurs.hpp" #include "path.hpp" namespace libdar { /// \addtogroup API /// @{ /// the generic class, parent of all masks /// this is a pure virtual class that is used in API call /// any of the following mask classes inherit from this class class mask { public : mask() {}; mask(const mask & ref) = default; mask(mask && ref) noexcept = default; mask & operator = (const mask & ref) = default; mask & operator = (mask && ref) noexcept = default; virtual ~mask() = default; /// check wether the given string is covered by the mask /// \param[in] expression is the filename to check /// \return true if the given filename is covered by the mask /// \note only libdar internally needs to call this method virtual bool is_covered(const std::string &expression) const = 0; /// check whether the given path is covered by the mask /// \param[in] chemin is the path to check /// \return true if the given path is covered by the mask /// \note only libdar internally needs to call this method /// \note this is an optional method to the previous one, it can be overwritten virtual bool is_covered(const path & chemin) const { return is_covered(chemin.display()); }; /// dump in human readable form the nature of the mask /// \param[in] prefix used for indentation withing the output string virtual std::string dump(const std::string & prefix = "") const = 0; /// this is to be able to copy a mask without knowing its /// exact class and without loosing its specialized data virtual mask *clone() const = 0; }; /// boolean mask, either always true or false /// it matches all files or no files at all class bool_mask : public mask { public : /// the constructor /// \param[in] always is the value that this mask will always return /// when the is_covered method will be used /// \note once initialized an object cannot change its behavior bool_mask(bool always) { val = always; }; bool_mask(const bool_mask & ref) = default; bool_mask(bool_mask && ref) noexcept = default; bool_mask & operator = (const bool_mask & ref) = default; bool_mask & operator = (bool_mask && ref) noexcept = default; ~bool_mask() = default; /// inherited from the mask class bool is_covered(const std::string & expression) const override { return val; }; bool is_covered(const path & chemin) const override { return val; }; std::string dump(const std::string & prefix) const override { return prefix + (val ? gettext("TRUE") : gettext("FALSE")); }; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) bool_mask(val); }; private : bool val; }; /// matches as done on shell command lines (see "man 7 glob") class simple_mask : public mask { public : /// the constructor to use by libdar external programs /// \param[in] wilde_card_expression is the glob expression that defines the mask /// \param[in] case_sensit whether the mask is case sensitive or not simple_mask(const std::string & wilde_card_expression, bool case_sensit); /// copy constructor simple_mask(const simple_mask & m) = default; /// move constructor simple_mask(simple_mask && ref) noexcept = default; /// assignment operator simple_mask & operator = (const simple_mask & m) = default; /// move operator simple_mask & operator = (simple_mask && ref) noexcept = default; /// default destructor ~simple_mask() = default; /// inherited from the mask class bool is_covered(const std::string &expression) const override; /// inherited from the mask class std::string dump(const std::string & prefix) const override; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) simple_mask(*this); }; private : std::string the_mask; bool case_s; }; /// matches regular expressions (see "man 7 regex") class regular_mask : public mask { public : /// the constructor to be used by libdar external programs /// \param[in] wilde_card_expression is the regular expression that defines the mask /// \param[in] x_case_sensit whether the mask is case sensitive or not regular_mask(const std::string & wilde_card_expression, bool x_case_sensit); /// the copy constructor regular_mask(const regular_mask & ref): mask(ref) { copy_from(ref); }; /// the move constructor regular_mask(regular_mask && ref) noexcept: mask(std::move(ref)) { move_from(std::move(ref)); }; /// the assignment operator regular_mask & operator = (const regular_mask & ref); /// the move operator regular_mask & operator = (regular_mask && ref) noexcept; /// destructor virtual ~regular_mask() { detruit(); }; /// inherited from the mask class bool is_covered(const std::string & expression) const override; /// inherited from the mask class std::string dump(const std::string & prefix) const override; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) regular_mask(*this); }; private : regex_t preg; std::string mask_exp; ///< used only by the copy constructor bool case_sensit; ///< used only by the copy constructor void set_preg(const std::string & wilde_card_expression, bool x_case_sensit); void copy_from(const regular_mask & ref); void move_from(regular_mask && ref) noexcept; void detruit() noexcept { regfree(&preg); }; }; /// negation of another mask /// this is the first mask built over masks /// it realizes the negation of the given mask class not_mask : public mask { public : /// the constructor to be used by libdar external programs /// \param[in] m is the mask to negate /// \note the mask used as argument need not to survive the just created not_mask object /// as an internal copy of the mask given in argument has been done. not_mask(const mask &m) { copy_from(m); }; /// copy constructor not_mask(const not_mask & m) : mask(m) { copy_from(m); }; /// move constructor not_mask(not_mask && m) noexcept: mask(std::move(m)) { nullifyptr(); move_from(std::move(m)); }; /// assignment operator not_mask & operator = (const not_mask & m); /// move operator not_mask & operator = (not_mask && m) noexcept { move_from(std::move(m)); return *this; }; /// destructor ~not_mask() { detruit(); }; /// inherited from the mask class bool is_covered(const std::string &expression) const override { return !ref->is_covered(expression); }; bool is_covered(const path & chemin) const override { return !ref->is_covered(chemin); }; std::string dump(const std::string & prefix) const override; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) not_mask(*this); }; private : mask *ref; void nullifyptr() noexcept { ref = nullptr; }; void copy_from(const not_mask &m); void copy_from(const mask &m); void move_from(not_mask && ref) noexcept; void detruit(); }; /// makes an *AND* operator between two or more masks class et_mask : public mask { public : /// the constructor to be used by libdar external programs /// \note at this stage the mask is not usable and will /// throw an exception until some mask are added to the *AND* /// thanks to the add_mask() method et_mask() {}; // field "lst" is an object and initialized by its default constructor /// copy constructor et_mask(const et_mask &m) : mask(m) { copy_from(m); }; /// move constructor et_mask(et_mask && m) noexcept: mask(std::move(m)) { move_from(std::move(m)); }; /// assignment operator et_mask & operator = (const et_mask &m); /// move operator et_mask & operator = (et_mask && m) noexcept { mask::operator = (std::move(m)); move_from(std::move(m)); return *this; }; /// destructor ~et_mask() { detruit(); }; /// add a mask to the operator /// \param[in] toadd a mask to add to the *AND* operator /// \note the mask given in argument has not to survive the et_mask to which it has been added /// a internal copy of the mask has been done. void add_mask(const mask & toadd); /// inherited from the mask class bool is_covered(const std::string & expression) const override { return t_is_covered(expression); }; bool is_covered(const path & chemin) const override { return t_is_covered(chemin); }; std::string dump(const std::string & prefix) const override { return dump_logical(prefix, gettext("AND")); }; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) et_mask(*this); }; /// the number of mask on which is done the *AND* operator /// \return the number of mask that has been added thanks to the add_mask() method /// \note there is no mean to remove a given mask once it has been added (see the clear method) U_I size() const { return lst.size(); }; /// clear the mask /// remove all previously added masks /// \note that after this call the mask is no more usable as the *AND* operator cannot be done /// on any mask void clear() { detruit(); }; protected : std::deque lst; std::string dump_logical(const std::string & prefix, const std::string & boolop) const; private : void copy_from(const et_mask & m); void move_from(et_mask && m) noexcept; void detruit(); template bool t_is_covered(const T & expression) const { std::deque::const_iterator it = lst.begin(); if(lst.empty()) throw Erange("et_mask::is_covered", dar_gettext("No mask in the list of mask to operate on")); while(it != lst.end() && (*it)->is_covered(expression)) ++it; return it == lst.end(); } }; /// makes the *OR* operator between two or more masks /// this mask has exactly the same use as the et_mask /// please see the et_mask documentation. The only difference /// is that it makes an *OR* operation rather than an *AND* /// with the masks added thanks to the add_mask method class ou_mask : public et_mask { public: ou_mask() {}; ou_mask(const ou_mask & ref) = default; ou_mask(ou_mask && ref) noexcept = default; ou_mask & operator = (const ou_mask & ref) = default; ou_mask & operator = (ou_mask && ref) noexcept = default; ~ou_mask() = default; /// inherited from the mask class bool is_covered(const std::string & expression) const override { return t_is_covered(expression); }; bool is_covered(const path & chemin) const override { return t_is_covered(chemin); }; std::string dump(const std::string & prefix) const override { return dump_logical(prefix, gettext("OR")); }; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) ou_mask(*this); }; private: template bool t_is_covered(const T & expression) const { std::deque::const_iterator it = lst.begin(); if(lst.empty()) throw Erange("et_mask::is_covered", dar_gettext("No mask to operate on in the list of mask")); while(it != lst.end() && ! (*it)->is_covered(expression)) it++; return it != lst.end(); } }; /// string matches if it is subdir of mask or mask is a subdir of expression class simple_path_mask : public mask { public : /// the constructor to be used by libdar external programs /// \param[in] p the path the compare with /// \param[in] case_sensit whether the mask is case sensitive or not /// \note p must be a valid path simple_path_mask(const path &p, bool case_sensit) : chemin(p) { case_s = case_sensit; }; simple_path_mask(const simple_path_mask & ref) = default; simple_path_mask(simple_path_mask && ref) noexcept = default; simple_path_mask & operator = (const simple_path_mask & ref) = default; simple_path_mask & operator = (simple_path_mask && ref) noexcept = default; ~simple_path_mask() = default; /// inherited from the mask class bool is_covered(const std::string & expression) const override { throw SRC_BUG; }; bool is_covered(const path & chemin) const override; std::string dump(const std::string & prefix) const override; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) simple_path_mask(*this); }; private : path chemin; bool case_s; }; /// matches if string is exactly the given mask (no wilde card expression) class same_path_mask : public mask { public : /// the constructor to be used by libdar external programs /// \param[in] p is the path to compare with /// \param[in] case_sensit whether the mask is case sensitive or not same_path_mask(const std::string &p, bool case_sensit) { chemin = p; case_s = case_sensit; }; same_path_mask(const same_path_mask & ref) = default; same_path_mask(same_path_mask && ref) noexcept = default; same_path_mask & operator = (const same_path_mask & ref) = default; same_path_mask & operator = (same_path_mask && ref) noexcept = default; ~same_path_mask() = default; /// inherited from the mask class bool is_covered(const std::string &chemin) const override; /// inherited from the mask class std::string dump(const std::string & prefix) const override; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) same_path_mask(*this); }; private : std::string chemin; bool case_s; }; /// matches if string is the given constructor string or a sub directory of it class exclude_dir_mask : public mask { public: /// the constructor to be used by libdar external programs /// \param[in] p is the path to compare with /// \param[in] case_sensit whether the mask is case sensitive or not exclude_dir_mask(const std::string &p, bool case_sensit) { chemin = p; case_s = case_sensit;}; exclude_dir_mask(const exclude_dir_mask & ref) = default; exclude_dir_mask(exclude_dir_mask && ref) noexcept = default; exclude_dir_mask & operator = (const exclude_dir_mask & ref) = default; exclude_dir_mask & operator = (exclude_dir_mask && ref) noexcept = default; ~exclude_dir_mask() = default; /// inherited from the mask class bool is_covered(const std::string &expression) const override { throw SRC_BUG; } bool is_covered(const path &chemin) const override { return chemin.is_subdir_of(chemin, case_s); }; std::string dump(const std::string & prefix) const override; /// inherited from the mask class mask *clone() const override { return new (std::nothrow) exclude_dir_mask(*this); }; private: std::string chemin; bool case_s; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/label.hpp0000644000175000017500000000516714636066467013144 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file label.hpp /// \brief define the datastructure "label" used to identify slice membership to an archive /// \ingroup Private #ifndef LABEL_HPP #define LABEL_HPP #include "../my_config.h" #include "integers.hpp" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// manage label data structure used in archive slice headers class label { public: label(); // builds a label equal to 'zero' label(const label & ref) { copy_from(ref); }; label(label && ref) noexcept { move_from(std::move(ref)); }; label & operator = (const label & ref) { copy_from(ref); return *this; }; label & operator = (label && ref) noexcept { move_from(std::move(ref)); return *this; }; ~label() = default; bool operator == (const label & ref) const; bool operator != (const label & ref) const { return ! ((*this) == ref); }; void clear(); bool is_cleared() const; void generate_internal_filename(); void read(generic_file & f); void dump(generic_file & f) const; void invert_first_byte() { val[0] = ~val[0]; }; // avoid using these two calls, only here for backward compatibility // where the cost to move to object is really too heavy than // sticking with a char array. U_I size() const { return LABEL_SIZE; }; char *data() { return (char *)&val; }; const char *data() const { return (char *)&val; }; static U_I common_size() { return LABEL_SIZE; }; private: static constexpr U_I LABEL_SIZE = 10; char val[LABEL_SIZE]; void copy_from(const label & ref); void move_from(label && ref) noexcept; }; extern const label label_zero; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/secu_memory_file.hpp0000644000175000017500000000706314636066467015410 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file secu_memory_file.hpp /// \brief secu_memory_file is a generic_file class that only uses secured memory (not swappable and zeroed after use) /// \ingroup Private #ifndef SECU_MEMORY_FILE_HPP #define SECU_MEMORY_FILE_HPP #include "generic_file.hpp" #include "storage.hpp" #include "secu_string.hpp" namespace libdar { /// \addtogroup Private /// @{ class secu_memory_file : public generic_file { public: // Constructors & Destructor secu_memory_file(U_I storage_size) : generic_file(gf_read_write), data(storage_size) { position = 0; }; secu_memory_file(const secu_memory_file & ref) = default; secu_memory_file(secu_memory_file && ref) noexcept = default; secu_memory_file & operator = (const secu_memory_file & ref) = default; secu_memory_file & operator = (secu_memory_file && ref) noexcept = default; ~secu_memory_file() = default; // memory_storage specific methods /// reset the storage size and empty object content void reset(U_I size) { if(is_terminated()) throw SRC_BUG; position = 0; data.resize(size); }; /// the size of the data in the object infinint get_size() const { return data.get_size(); }; /// the allocated size of the object infinint get_allocated_size() const { return data.get_allocated_size(); }; /// set the content to a random string of size bytes void randomize(U_I size) { if(size > data.get_allocated_size()) reset(size); data.randomize(size); }; // virtual method inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override { return true; }; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return false; }; virtual infinint get_position() const override { if(is_terminated()) throw SRC_BUG; return position; }; const secu_string & get_contents() const { return data; }; protected: // virtual method inherited from generic_file virtual void inherited_read_ahead(const infinint & amount) override {}; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override { throw Efeature("truncate a secu_memory_file object"); }; virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override {}; private: secu_string data; U_I position; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/libdar_slave.hpp0000644000175000017500000000721414636066467014507 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file libdar_slave.hpp /// \brief API for dar_slave functionnality /// \ingroup API #ifndef LIBDAR_SLAVE_HPP #define LIBDAR_SLAVE_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "user_interaction.hpp" namespace libdar { /// \addtogroup API /// @{ /// class implementing the dar_slave feature class libdar_slave { public: /// libdar_slave constructor /// \param[in] dialog for user interaction. Can be set to nullptr /// \param[in] folder is the directory where resides the backup to read /// \param[in] basename is the backup basename /// \param[in] extension should be set to "dar" /// \param[in] input_pipe_is_fd if true the input_pipe argument is expected to be an /// integer (a file descriptor open for reading) /// \param[in] input_pipe is the name of the pipe order will come from or a filedescriptor if input_pipe_is_fd is true /// \param[in] output_pipe_is_fd if true the output_pipe argument is expected to be /// an integer (a file descriptor open for writing) /// \param[in] output_pipe is the name of the pipe to send data to dar or a filedescriptor depending /// on output_pipe_is_fd value /// \param[in] execute is a command to execute before reading a new slice, same macro substition is available as /// libdar::archive::set_execute() /// \param[in] min_digits minimum digits used to create the archive. Set it to zero if this option was not used /// at archive creation time /// \note if input_pipe is an empty string stdin is used /// \note if output_pipe is an empty string stdout is used libdar_slave(std::shared_ptr & dialog, const std::string & folder, const std::string & basename, const std::string & extension, bool input_pipe_is_fd, const std::string & input_pipe, bool output_pipe_is_fd, const std::string & output_pipe, const std::string & execute, const infinint & min_digits); libdar_slave(const libdar_slave & ref) = delete; libdar_slave(libdar_slave && ref) noexcept = default; libdar_slave & operator = (const libdar_slave & ref) = delete; libdar_slave & operator = (libdar_slave && ref) noexcept = default; ~libdar_slave(); /// enslave this object to the dar process through the created pipes /// \note run() will return when dar will no more need of this slave. /// if you need to abort this run(), let dar abort properly this will do the /// expected result properly. void run(); private: class i_libdar_slave; std::unique_ptr pimpl; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/shell_interaction_emulator.hpp0000644000175000017500000000470214636066467017475 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file shell_interaction_emulator.hpp /// \brief wrapper class that given a user_interaction send it the shell_interaction formatted output /// \ingroup API #ifndef SHELL_INTERACTION_EMULATOR_HPP #define SHELL_INTERACTION_EMULATOR_HPP extern "C" { } // end extern "C" #include "../my_config.h" #include "shell_interaction.hpp" namespace libdar { /// \addtogroup API /// @{ class shell_interaction_emulator : public shell_interaction { public: shell_interaction_emulator(user_interaction *emulator); shell_interaction_emulator(const shell_interaction_emulator & ref) = default; shell_interaction_emulator(shell_interaction_emulator && ref) noexcept = delete; shell_interaction_emulator & operator = (const shell_interaction_emulator & ref) = delete; shell_interaction_emulator & operator = (shell_interaction_emulator && ref) noexcept = delete; virtual ~shell_interaction_emulator() = default; protected: virtual void inherited_message(const std::string & message) override { emul->message(message); }; virtual bool inherited_pause(const std::string & message) override; virtual std::string inherited_get_string(const std::string & message, bool echo) override { return emul->get_string(message, echo); }; virtual secu_string inherited_get_secu_string(const std::string & message, bool echo) override { return emul->get_secu_string(message, echo); }; private: user_interaction *emul; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/generic_file_overlay_for_gpgme.cpp0000644000175000017500000000710014636066467020246 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "tools.hpp" #include "generic_file_overlay_for_gpgme.hpp" namespace libdar { #ifdef GPGME_SUPPORT static ssize_t gpgme_data_read_cb(void *handle, void *buffer, size_t size); static ssize_t gpgme_data_write_cb(void *handle, const void *buffer, size_t size); static off_t gpgme_data_seek_cb(void *handle, off_t offset, int whence); static void gpgme_data_release_cb(void *handle); #endif generic_file_overlay_for_gpgme::generic_file_overlay_for_gpgme(generic_file *f) { #ifdef GPGME_SUPPORT gpgme_error_t err; if(f == nullptr) throw SRC_BUG; below = f; cbs.read = &gpgme_data_read_cb; cbs.write = &gpgme_data_write_cb; cbs.seek = &gpgme_data_seek_cb; cbs.release = &gpgme_data_release_cb; err = gpgme_data_new_from_cbs(&handle, &cbs, this); if(gpgme_err_code(err) != GPG_ERR_NO_ERROR) { throw Erange("generic_file_overlay_for_gpgme::generi_file_overlay_for_gpgme", tools_printf(gettext("Error creating data buffer overlay for GPGME: %s"), tools_gpgme_strerror_r(err).c_str())); } #else throw Efeature("Asymetric Strong encryption algorithms using GPGME"); #endif } #if GPGME_SUPPORT static ssize_t gpgme_data_read_cb(void *handle, void *buffer, size_t size) { generic_file_overlay_for_gpgme *obj = (generic_file_overlay_for_gpgme *)(handle); return obj->get_below()->read((char*)buffer, size); } static ssize_t gpgme_data_write_cb(void *handle, const void *buffer, size_t size) { generic_file_overlay_for_gpgme *obj = (generic_file_overlay_for_gpgme *)(handle); obj->get_below()->write((char*)buffer, size); return size; } static off_t gpgme_data_seek_cb(void *handle, off_t offset, int whence) { generic_file_overlay_for_gpgme *obj = (generic_file_overlay_for_gpgme *)(handle); off_t ret; switch(whence) { case SEEK_SET: obj->get_below()->skip(infinint(offset)); break; case SEEK_CUR: obj->get_below()->skip_relative(offset); break; case SEEK_END: obj->get_below()->skip_to_eof(); obj->get_below()->skip_relative(offset); default: throw SRC_BUG; } if(whence == SEEK_SET) ret = offset; else { infinint tmp = obj->get_below()->get_position(); ret = 0; tmp.unstack(ret); if(!tmp.is_zero()) throw Erange("gpgme_data_seek_cb", gettext("File offset too large to be stored in off_t type")); } return ret; } static void gpgme_data_release_cb(void *handle) { // nothing to do // the generic_file_overlay_for_gpgme object // has no internal data to be released by gpgme } #endif } // end of namespace dar-2.7.15/src/libdar/cat_status.hpp0000644000175000017500000000643514636067146014231 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_status.hpp /// \brief the different status of data and EA /// \ingroup Private /// \note API included module due to dependencies #ifndef CAT_STATUS_HPP #define CAT_STATUS_HPP #include "../my_config.h" extern "C" { } // end extern "C" namespace libdar { /// \addtogroup Private /// @{ /// data saved status for an entry enum class saved_status { saved, ///< inode is saved in the archive inode_only, ///< data is not saved but inode meta data has changed since the archive of reference fake, ///< inode is not saved in the archive but is in the archive of reference (isolation context) s_fake is no more used in archive format "08" and above: isolated catalogue do keep the data pointers and s_saved stays a valid status in isolated catalogues. not_saved, ///< inode is not saved in the archive delta ///< inode is saved but as delta binary from the content (delta signature) of what was found in the archive of reference }; /// EA saved status for an entry enum class ea_saved_status { none, ///< no EA present for this inode in filesystem partial, ///< EA present in filesystem but not stored (ctime used to check changes) fake, ///< EA present in filesystem but not attached to this inode (isolation context) no more used in archive version "08" and above, ea_partial or ea_full stays a valid status in isolated catalogue because pointers to EA and data are no more removed during isolation process. full, ///< EA present in filesystem and attached to this inode removed ///< EA were present in the reference version, but not present anymore }; /// FSA saved status for an entry /// there is not "remove status for FSA, either the cat_inode contains /// full copy of FSA or only remembers the families of FSA found in the unchanged cat_inode /// FSA none is used when the file has no FSA because: /// - either the underlying filesystem has no known FSA /// - or the underlying filesystem FSA support has not been activated at compilation time /// - or the fsa_scope requested at execution time exclude the filesystem FSA families available here enum class fsa_saved_status { none, ///< no FSA saved partial, ///< FSA unchanged, not fully stored full ///< FSA saved }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_specific_attribute.cpp0000644000175000017500000010513614636066467020171 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif #ifdef LIBDAR_NODUMP_FEATURE #if HAVE_SYS_IOCTL_H #include #endif #if LIBDAR_NODUMP_FEATURE == NODUMP_LINUX #include #else #if LIBDAR_NODUMP_FEATURE == NODUMP_EXT2FS #include #else #error "unknown location of ext2_fs.h include file" #endif #endif #endif #if HAVE_UNISTD_H #include #endif } #include #include "integers.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "filesystem_tools.hpp" #include "filesystem_specific_attribute.hpp" #include "cygwin_adapt.hpp" #include "deci.hpp" #include "fichier_local.hpp" #include "compile_time_features.hpp" #include "capabilities.hpp" #include "erreurs.hpp" #include "archive_version.hpp" using namespace std; namespace libdar { static bool compare_for_sort(const filesystem_specific_attribute *a, const filesystem_specific_attribute *b); template bool binary_search_in_sorted_list(const deque & table, const T *val, U_I & index) { U_I min = 0; U_I max = table.size(); if(val == nullptr) throw SRC_BUG; if(max == 0) // empty table return false; do { index = (min + max)/2; if(table[index] == nullptr) throw SRC_BUG; if(*(table[index]) < *val) min = index + 1; else max = index; } while(!table[index]->is_same_type_as(*val) && max - min > 0); if(max - min <= 0) index = min; return min < table.size() && (table[index])->is_same_type_as(*val); } /////////////////////////////////////////////////////////////////////////////////// bool filesystem_specific_attribute::is_same_type_as(const filesystem_specific_attribute & ref) const { return get_family() == ref.get_family() && get_nature() == ref.get_nature(); } bool filesystem_specific_attribute::operator < (const filesystem_specific_attribute & ref) const { if(fam < ref.fam) return true; else if(fam > ref.fam) return false; else return nat < ref.nat; } /////////////////////////////////////////////////////////////////////////////////// const U_I FAM_SIG_WIDTH = 1; const U_I NAT_SIG_WIDTH = 2; static bool compare_for_sort(const filesystem_specific_attribute *a, const filesystem_specific_attribute *b) { if(a == nullptr || b == nullptr) throw SRC_BUG; return *a < *b; } void filesystem_specific_attribute_list::clear() { deque::iterator it = fsa.begin(); while(it != fsa.end()) { if(*it != nullptr) { delete *it; *it = nullptr; } ++it; } fsa.clear(); } void filesystem_specific_attribute_list::add(const filesystem_specific_attribute & fsa) { priv_add(fsa); update_familes(); sort_fsa(); } bool filesystem_specific_attribute_list::is_included_in(const filesystem_specific_attribute_list & ref, const fsa_scope & scope) const { bool ret = true; deque::const_iterator it = fsa.begin(); deque::const_iterator rt = ref.fsa.begin(); while(ret && it != fsa.end()) { if(rt == ref.fsa.end()) { ret = false; continue; // skip the rest of the while loop } if(*it == nullptr) throw SRC_BUG; if(*rt == nullptr) throw SRC_BUG; if(scope.find((*it)->get_family()) == scope.end()) { // this FSA is out of the scope, skipping it ++it; continue; // skip the rest of the while loop } while(rt != ref.fsa.end() && *(*rt) < *(*it)) { ++rt; if(*rt == nullptr) throw SRC_BUG; } if(rt == ref.fsa.end()) ret = false; else if(*(*rt) == *(*it)) ++it; else ret = false; } return ret; } void filesystem_specific_attribute_list::read(generic_file & f, archive_version ver) { infinint size = infinint(f); U_I sub_size; U_I tmp; do { sub_size = 0; size.unstack(sub_size); if(size > 0 && sub_size == 0) throw SRC_BUG; while(sub_size > 0) { char buffer[FAM_SIG_WIDTH + NAT_SIG_WIDTH + 1]; fsa_family fam; fsa_nature nat; filesystem_specific_attribute *ptr = nullptr; tmp = f.read(buffer, FAM_SIG_WIDTH); if(tmp < FAM_SIG_WIDTH) throw Erange("filesystem_specific_attribute_list::read", gettext("invalid length for FSA family flag")); buffer[FAM_SIG_WIDTH] = '\0'; fam = signature_to_family(buffer); tmp = f.read(buffer, NAT_SIG_WIDTH); if(tmp < NAT_SIG_WIDTH) throw Erange("filesystem_specific_attribute_list::read", gettext("invalid length for FSA nature flag")); buffer[NAT_SIG_WIDTH] = '\0'; nat = signature_to_nature(buffer); switch(nat) { case fsan_unset: throw SRC_BUG; case fsan_creation_date: ptr = new (nothrow) fsa_time(f, ver, fam, nat); break; case fsan_append_only: case fsan_compressed: case fsan_no_dump: case fsan_immutable: case fsan_data_journaling: case fsan_secure_deletion: case fsan_no_tail_merging: case fsan_undeletable: case fsan_noatime_update: case fsan_synchronous_directory: case fsan_synchronous_update: case fsan_top_of_dir_hierarchy: ptr = new (nothrow) fsa_bool(f, fam, nat); break; default: throw SRC_BUG; } if(ptr == nullptr) throw Ememory("filesystem_specific_attribute_list::read"); fsa.push_back(ptr); ptr = nullptr; --sub_size; } } while(!size.is_zero()); update_familes(); sort_fsa(); } void filesystem_specific_attribute_list::write(generic_file & f) const { infinint size = fsa.size(); deque::const_iterator it = fsa.begin(); size.dump(f); while(it != fsa.end()) { string tmp; if(*it == nullptr) throw SRC_BUG; tmp = family_to_signature((*it)->get_family()); f.write(tmp.c_str(), tmp.size()); tmp = nature_to_signature((*it)->get_nature()); f.write(tmp.c_str(), tmp.size()); (*it)->write(f); ++it; } } void filesystem_specific_attribute_list::get_fsa_from_filesystem_for(user_interaction & ui, const string & target, const fsa_scope & scope, mode_t itype, bool auto_zeroing_neg_dates) { clear(); if(scope.find(fsaf_hfs_plus) != scope.end()) { fill_HFS_FSA_with(ui, target, itype, auto_zeroing_neg_dates); } if(scope.find(fsaf_linux_extX) != scope.end()) { fill_extX_FSA_with(target, itype); } update_familes(); sort_fsa(); } bool filesystem_specific_attribute_list::has_linux_immutable_set() const { const filesystem_specific_attribute *ptr = nullptr; const fsa_bool *bptr = nullptr; if(find(fsaf_linux_extX, fsan_immutable, ptr)) { bptr = dynamic_cast(ptr); if(bptr == nullptr) throw SRC_BUG; // immutable flag should be a boolean return bptr->get_value(); } else // immutable attribute not present return false; } bool filesystem_specific_attribute_list::set_fsa_to_filesystem_for(const string & target, const fsa_scope & scope, user_interaction & ui, bool set_linux_immutable) const { bool ret = false; if(scope.find(fsaf_linux_extX) != scope.end()) ret |= set_extX_FSA_to(ui, target, set_linux_immutable); if(!set_linux_immutable && scope.find(fsaf_hfs_plus) != scope.end()) ret |= set_hfs_FSA_to(ui, target); return ret; } const filesystem_specific_attribute & filesystem_specific_attribute_list::operator [] (U_I arg) const { if(arg >= fsa.size()) throw SRC_BUG; if(fsa[arg] == nullptr) throw SRC_BUG; return *(fsa[arg]); } infinint filesystem_specific_attribute_list::storage_size() const { infinint ret = infinint(size()).get_storage_size(); deque::const_iterator it = fsa.begin(); infinint overhead = family_to_signature(fsaf_hfs_plus).size() + nature_to_signature(fsan_creation_date).size(); while(it != fsa.end()) { if(*it == nullptr) throw SRC_BUG; ret += (*it)->storage_size() + overhead; ++it; } return ret; } filesystem_specific_attribute_list filesystem_specific_attribute_list::operator + (const filesystem_specific_attribute_list & arg) const { filesystem_specific_attribute_list ret = *this; deque::const_iterator it = arg.fsa.begin(); while(it != arg.fsa.end()) { if(*it == nullptr) throw SRC_BUG; ret.priv_add(*(*it)); ++it; } ret.update_familes(); ret.sort_fsa(); return ret; } bool filesystem_specific_attribute_list::find(fsa_family fam, fsa_nature nat, const filesystem_specific_attribute *&ptr) const { fsa_bool tmp = fsa_bool(fam, nat, true); U_I index; if(binary_search_in_sorted_list(fsa, (filesystem_specific_attribute *)(&tmp), index)) { ptr = fsa[index]; return true; } else return false; } void filesystem_specific_attribute_list::copy_from(const filesystem_specific_attribute_list & ref) { deque::const_iterator it = ref.fsa.begin(); fsa.clear(); while(it != ref.fsa.end()) { if(*it == nullptr) throw SRC_BUG; fsa.push_back((*it)->clone()); ++it; } familes = ref.familes; } void filesystem_specific_attribute_list::update_familes() { deque::iterator it = fsa.begin(); familes.clear(); while(it != fsa.end()) { if(*it == nullptr) throw SRC_BUG; familes.insert((*it)->get_family()); ++it; } } void filesystem_specific_attribute_list::priv_add(const filesystem_specific_attribute & ref) { U_I index = 0; if(binary_search_in_sorted_list(fsa, &ref, index)) { if(fsa[index] == nullptr) throw SRC_BUG; else { filesystem_specific_attribute *rep = ref.clone(); if(rep == nullptr) throw Ememory("filesystem_specific_attribute_list::add"); try { delete fsa[index]; fsa[index] = rep; } catch(...) { delete rep; throw; } } } else { filesystem_specific_attribute *rep = ref.clone(); if(rep == nullptr) throw Ememory("filesystem_specific_attribute_list::add"); try { fsa.resize(fsa.size()+1, nullptr); for(U_I i = fsa.size()-1 ; i > index ; --i) { fsa[i] = fsa[i-1]; fsa[i-1] = nullptr; } fsa[index] = rep; } catch(...) { delete rep; throw; } } } void filesystem_specific_attribute_list::sort_fsa() { sort(fsa.begin(), fsa.end(), compare_for_sort); } template void create_or_throw(T *& ref, fsa_family f, fsa_nature n, const U & val) { if(ref != nullptr) throw SRC_BUG; ref = new (nothrow) T(f, n, val); if(ref == nullptr) throw Ememory("template create_or_throw"); } void filesystem_specific_attribute_list::fill_extX_FSA_with(const std::string & target, mode_t itype) { #ifdef LIBDAR_NODUMP_FEATURE S_I fd = -1; // symlink do not have their own extX FSA: restoring FSA of a symlink change the FSA of the target of the symlink if(S_ISREG(itype) || S_ISDIR(itype)) { try { fichier_local ftmp = fichier_local(target, compile_time::furtive_read()); fd = ftmp.give_fd_and_terminate(); } catch(Egeneric & e) { if(!compile_time::furtive_read()) throw; // not a problem about furtive read mode try // trying openning not using furtive read mode { fichier_local ftmp = fichier_local(target, false); fd = ftmp.give_fd_and_terminate(); } catch(Egeneric & e) { fd = -1; // we assume this FSA family is not supported for that file } } if(fd < 0) return; // silently aborting assuming FSA family not supported for that file try { S_I f = 0; fsa_bool * ptr = nullptr; if(ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) { close(fd); return; // assuming there is no support for that FSA family } #ifdef EXT2_APPEND_FL create_or_throw(ptr, fsaf_linux_extX, fsan_append_only, (f & EXT2_APPEND_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_COMPR_FL create_or_throw(ptr, fsaf_linux_extX, fsan_compressed, (f & EXT2_COMPR_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_NODUMP_FL create_or_throw(ptr, fsaf_linux_extX, fsan_no_dump, (f & EXT2_NODUMP_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_IMMUTABLE_FL create_or_throw(ptr, fsaf_linux_extX, fsan_immutable, (f & EXT2_IMMUTABLE_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT3_JOURNAL_DATA_FL create_or_throw(ptr, fsaf_linux_extX, fsan_data_journaling, (f & EXT3_JOURNAL_DATA_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #else #ifdef EXT2_JOURNAL_DATA_FL create_or_throw(ptr, fsaf_linux_extX, fsan_data_journaling, (f & EXT2_JOURNAL_DATA_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #endif #ifdef EXT2_SECRM_FL create_or_throw(ptr, fsaf_linux_extX, fsan_secure_deletion, (f & EXT2_SECRM_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_NOTAIL_FL create_or_throw(ptr, fsaf_linux_extX, fsan_no_tail_merging, (f & EXT2_NOTAIL_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_UNRM_FL create_or_throw(ptr, fsaf_linux_extX, fsan_undeletable, (f & EXT2_UNRM_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_NOATIME_FL create_or_throw(ptr, fsaf_linux_extX, fsan_noatime_update, (f & EXT2_NOATIME_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_DIRSYNC_FL create_or_throw(ptr, fsaf_linux_extX, fsan_synchronous_directory, (f & EXT2_DIRSYNC_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_SYNC_FL create_or_throw(ptr, fsaf_linux_extX, fsan_synchronous_update, (f & EXT2_SYNC_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif #ifdef EXT2_TOPDIR_FL create_or_throw(ptr, fsaf_linux_extX, fsan_top_of_dir_hierarchy, (f & EXT2_TOPDIR_FL) != 0); fsa.push_back(ptr); ptr = nullptr; #endif } catch(...) { close(fd); throw; } close(fd); datetime linux_birthtime; if(filesystem_tools_read_linux_birthtime(target, linux_birthtime)) { fsa_time *date_ptr = nullptr; create_or_throw(date_ptr, fsaf_linux_extX, fsan_creation_date, linux_birthtime); fsa.push_back(date_ptr); date_ptr = nullptr; } } #else // nothing to do, as this FSA has not been activated at compilation time #endif } void filesystem_specific_attribute_list::fill_HFS_FSA_with(user_interaction & ui, const std::string & target, mode_t itype, bool auto_zeroing_neg_dates) { #ifdef LIBDAR_BIRTHTIME struct stat tmp; int lu = stat(target.c_str(), &tmp); if(lu < 0) return; // silently aborting assuming FSA family not supported for that file else { fsa_time * ptr = nullptr; #if LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND || LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND tools_check_negative_date(tmp.st_birthtim.tv_sec, ui, target.c_str(), "birthtime", !auto_zeroing_neg_dates, auto_zeroing_neg_dates); #if LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND datetime birthtime = datetime(tmp.st_birthtim.tv_sec, tmp.st_birthtim.tv_nsec/1000, datetime::tu_microsecond); #else datetime birthtime = datetime(tmp.st_birthtim.tv_sec, tmp.st_birthtim.tv_nsec, datetime::tu_nanosecond); #endif if(birthtime.is_null()) // assuming an error avoids getting time that way birthtime = datetime(tmp.st_birthtime, 0, datetime::tu_second); #else tools_check_negative_date(tmp.st_birthtime, ui, target.c_str(), "birthtime", !auto_zeroing_neg_dates, auto_zeroing_neg_dates); datetime birthtime = datetime(tmp.st_birthtime, 0, datetime::tu_second); #endif create_or_throw(ptr, fsaf_hfs_plus, fsan_creation_date, birthtime); fsa.push_back(ptr); ptr = nullptr; } #endif } bool filesystem_specific_attribute_list::set_extX_FSA_to(user_interaction & ui, const std::string & target, bool set_immutable) const { bool ret = false; bool has_extX_FSA = false; deque::const_iterator it = fsa.begin(); while(!has_extX_FSA && it != fsa.end()) { if(*it == nullptr) throw SRC_BUG; if((*it)->get_family() == fsaf_linux_extX) has_extX_FSA = true; ++it; } #ifdef LIBDAR_NODUMP_FEATURE if(has_extX_FSA) { S_I fd = -1; try { bool furtive = capability_FOWNER(ui, false) == capa_set ? compile_time::furtive_read() : false; fichier_local ftmp = fichier_local(target, furtive); fd = ftmp.give_fd_and_terminate(); } catch(Egeneric & e) { throw Erange("filesystem_specific_attribute_list::fill_extX_FSA_with", string(gettext("Failed setting (opening) extX family FSA: ")) + e.get_message()); } if(fd < 0) throw SRC_BUG; try { S_I f = 0; // will contain the desirable flag bits field S_I f_orig = 0; // will contain the original flag bits field const fsa_bool *it_bool = nullptr; if(ioctl(fd, EXT2_IOC_GETFLAGS, &f_orig) < 0) throw Erange("filesystem_specific_attribute_list::fill_extX_FSA_with", string(gettext("Failed reading existing extX family FSA: ")) + tools_strerror_r(errno)); f = f_orig; for(it = fsa.begin() ; it != fsa.end() ; ++it) { if(*it == nullptr) throw SRC_BUG; it_bool = dynamic_cast(*it); if((*it)->get_family() == fsaf_linux_extX) { switch((*it)->get_nature()) { case fsan_unset: throw SRC_BUG; case fsan_creation_date: // note birthtime is not possible to restore under Linux (today), // but can be restored by libdar under BSD systems like MACOS X break; // nothing to do here, this attribute will be set by tools_make_date() case fsan_append_only: if(set_immutable) continue; if(it_bool == nullptr) throw SRC_BUG; // should be a boolean #ifdef EXT2_APPEND_FL if(it_bool->get_value()) f |= EXT2_APPEND_FL; else f &= ~EXT2_APPEND_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_compressed: if(set_immutable) continue; #ifdef EXT2_COMPR_FL if(it_bool->get_value()) f |= EXT2_COMPR_FL; else f &= ~EXT2_COMPR_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_no_dump: if(set_immutable) continue; #ifdef EXT2_NODUMP_FL if(it_bool->get_value()) f |= EXT2_NODUMP_FL; else f &= ~EXT2_NODUMP_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_immutable: if(!set_immutable) // if *NOT* set_immutable, here continue; #ifdef EXT2_IMMUTABLE_FL if(it_bool->get_value()) f |= EXT2_IMMUTABLE_FL; else f &= ~EXT2_IMMUTABLE_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_data_journaling: if(set_immutable) continue; #ifdef EXT3_JOURNAL_DATA_FL if(it_bool->get_value()) f |= EXT3_JOURNAL_DATA_FL; else f &= ~EXT3_JOURNAL_DATA_FL; #else #ifdef EXT2_JOURNAL_DATA_FL if(it_bool->get_value()) f |= EXT2_JOURNAL_DATA_FL; else f &= ~EXT2_JOURNAL_DATA_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif #endif break; case fsan_secure_deletion: if(set_immutable) continue; #ifdef EXT2_SECRM_FL if(it_bool->get_value()) f |= EXT2_SECRM_FL; else f &= ~EXT2_SECRM_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_no_tail_merging: if(set_immutable) continue; #ifdef EXT2_NOTAIL_FL if(it_bool->get_value()) f |= EXT2_NOTAIL_FL; else f &= ~EXT2_NOTAIL_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_undeletable: if(set_immutable) continue; #ifdef EXT2_UNRM_FL if(it_bool->get_value()) f |= EXT2_UNRM_FL; else f &= ~EXT2_UNRM_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_noatime_update: if(set_immutable) continue; #ifdef EXT2_NOATIME_FL if(it_bool->get_value()) f |= EXT2_NOATIME_FL; else f &= ~EXT2_NOATIME_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_synchronous_directory: if(set_immutable) continue; #ifdef EXT2_DIRSYNC_FL if(it_bool->get_value()) f |= EXT2_DIRSYNC_FL; else f &= ~EXT2_DIRSYNC_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; case fsan_synchronous_update: if(set_immutable) continue; #ifdef EXT2_SYNC_FL if(it_bool->get_value()) f |= EXT2_SYNC_FL; else f &= ~EXT2_SYNC_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string(fsan_append_only).c_str(), target.c_str()); #endif break; case fsan_top_of_dir_hierarchy: if(set_immutable) continue; #ifdef EXT2_TOPDIR_FL if(it_bool->get_value()) f |= EXT2_TOPDIR_FL; else f &= ~EXT2_TOPDIR_FL; #else ui.printf(gettext("Warning: FSA %s/%s support has not been found at compilation time, cannot restore it for inode %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), fsa_nature_to_string((*it)->get_nature()).c_str(), target.c_str()); #endif break; default: throw SRC_BUG; } } } // now that f has been totally computed // we must handle the point that some FSA flag // need specific privileged to be set or cleared S_I mask_IMMUT = 0; // will carry the mask for flags that need the IMMUTABLE capability S_I mask_SYS_RES = 0; // will carry the mask for flags that need the SYS_RESOURCE capability #ifdef EXT2_APPEND_FL mask_IMMUT |= EXT2_APPEND_FL; #endif #ifdef EXT2_IMMUTABLE_FL mask_IMMUT |= EXT2_IMMUTABLE_FL; #endif #ifdef EXT3_JOURNAL_DATA_FL mask_SYS_RES |= EXT3_JOURNAL_DATA_FL; #else #ifdef EXT3_JOURNAL_DATA_FL mask_SYS_RES |= EXT2_JOURNAL_DATA_FL; #endif #endif // now that masks have been computed, we will proceed // in several steps: // - first setting the flag that do not need any privileges (abort upon error) // - second set the flags that need IMMUTABLE capability (warn and continue upon error) // - third set the flags that need SYS_RESOURCE capability (warn and continue upon error) // STEP 1: non privileged flags S_I tmp_f = (f & ~mask_IMMUT & ~mask_SYS_RES) | (f_orig & (mask_IMMUT | mask_SYS_RES)); if(tmp_f != f_orig) { if(ioctl(fd, EXT2_IOC_SETFLAGS, &tmp_f) < 0) throw Erange("filesystem_specific_attribute_list::fill_extX_FSA_with", string(gettext("Failed set extX family FSA: ")) + tools_strerror_r(errno )); ret = true; // some flags have been set or cleared } f_orig = tmp_f; // f_orig has been modified with the new values of the non priviledged flag bits // STEP 2 : setting the IMMUTABLE flags only if((f & mask_IMMUT) != (f_orig & mask_IMMUT)) // some immutable flags need to be changed { tmp_f = (f & mask_IMMUT) | (f_orig & ~mask_IMMUT); // only diff is IMMUTABLE flags switch(capability_LINUX_IMMUTABLE(ui, true)) { case capa_set: case capa_unknown: if(ioctl(fd, EXT2_IOC_SETFLAGS, &tmp_f) < 0) { string tmp = tools_strerror_r(errno); ui.printf("Failed setting FSA extX IMMUTABLE flags for %s: %", target.c_str(), tmp.c_str()); } else { f_orig = tmp_f; // f_orig now integrates the IMMUTABLE flags that we could set ret = true; // some flags have been set or cleared } break; case capa_clear: ui.printf(gettext("Not setting FSA extX IMMUTABLE flags for %s due to of lack of capability"), target.c_str()); break; default: throw SRC_BUG; } } /////// setting the SYS_RESOURCE flags only if((f & mask_SYS_RES) != (f_orig & mask_SYS_RES)) // some SYS_RESOURCE flags need to be changed { tmp_f = (f & mask_SYS_RES) | (f_orig & ~mask_SYS_RES); // only diff is the SYS_RES flags switch(capability_SYS_RESOURCE(ui, true)) { case capa_set: case capa_unknown: if(ioctl(fd, EXT2_IOC_SETFLAGS, &tmp_f) < 0) { string tmp = tools_strerror_r(errno); ui.printf("Failed setting FSA extX SYSTEME RESOURCE flags for %s: %", target.c_str(), tmp.c_str()); } else { f_orig = tmp_f; // f_orig now integrates the SYS_RESOURCE flags that we could set ret = true; // some flags have been set or cleared } break; case capa_clear: ui.printf(gettext("Not setting FSA extX SYSTEM RESOURCE flags for %s due to of lack of capability"), target.c_str()); break; default: throw SRC_BUG; } } } catch(...) { close(fd); throw; } close(fd); } #else if(has_extX_FSA) { ui.printf(gettext("Warning! %s Filesystem Specific Attribute support have not been activated at compilation time and could not be restored for %s"), fsa_family_to_string(fsaf_linux_extX).c_str(), target.c_str()); } #endif return ret; } bool filesystem_specific_attribute_list::set_hfs_FSA_to(user_interaction & ui, const std::string & target) const { bool ret = false; // the birthtime is set with the different other dates of that inode, so // here we just check that this FSA list provides a birthtime info: const filesystem_specific_attribute *tmp = nullptr; ret = find(fsaf_hfs_plus, fsan_creation_date, tmp); #ifndef LIBDAR_BIRTHTIME if(ret) ui.printf(gettext("Birth Time attribute cannot be restored for %s because no FSA familly able to carry that attribute could be activated at compilation time."), target.c_str()); // here we just warn, the birthtime restoration will be tried (calling twice utime()), even // if dar has not been compiled with birthtime support. Birthtime support is necessary only to // read birthtime value of an inode // birthtime restoration is done by tools_make_date() called from filesystem_tools_make_date() #endif return ret; } string filesystem_specific_attribute_list::family_to_signature(fsa_family f) { string ret; switch(f) { case fsaf_hfs_plus: ret = "h"; break; case fsaf_linux_extX: ret = "l"; break; default: throw SRC_BUG; } if(ret.size() != FAM_SIG_WIDTH) throw SRC_BUG; if(ret == "X") throw SRC_BUG; // resevered for field extension if necessary in the future return ret; } string filesystem_specific_attribute_list::nature_to_signature(fsa_nature n) { string ret; switch(n) { case fsan_unset: throw SRC_BUG; case fsan_creation_date: ret = "aa"; break; case fsan_append_only: ret = "ba"; break; case fsan_compressed: ret = "bb"; break; case fsan_no_dump: ret = "bc"; break; case fsan_immutable: ret = "bd"; break; case fsan_data_journaling: ret = "be"; break; case fsan_secure_deletion: ret = "bf"; break; case fsan_no_tail_merging: ret = "bg"; break; case fsan_undeletable: ret = "bh"; break; case fsan_noatime_update: ret = "bi"; break; case fsan_synchronous_directory: ret = "bj"; break; case fsan_synchronous_update: ret = "bk"; break; case fsan_top_of_dir_hierarchy: ret = "bl"; break; default: throw SRC_BUG; } if(ret.size() != NAT_SIG_WIDTH) throw SRC_BUG; if(ret == "XX") throw SRC_BUG; // resevered for field extension if necessary in the future return ret; } fsa_family filesystem_specific_attribute_list::signature_to_family(const string & sig) { if(sig.size() != FAM_SIG_WIDTH) throw Erange("filesystem_specific_attribute_list::signature_to_family", gettext("invalid length for FSA family flag")); if(sig == "h") return fsaf_hfs_plus; if(sig == "l") return fsaf_linux_extX; if(sig == "X") // resevered for field extension if necessary in the future throw Erange("filesysttem_specific_attribute_list::signature_to_family", gettext("invalid FSA family flag")); throw Erange("filesysttem_specific_attribute_list::signature_to_family", gettext("invalid FSA family flag")); } fsa_nature filesystem_specific_attribute_list::signature_to_nature(const string & sig) { if(sig.size() != NAT_SIG_WIDTH) throw SRC_BUG; if(sig == "aa") return fsan_creation_date; if(sig == "ba") return fsan_append_only; if(sig == "bb") return fsan_compressed; if(sig == "bc") return fsan_no_dump; if(sig == "bd") return fsan_immutable; if(sig == "be") return fsan_data_journaling; if(sig == "bf") return fsan_secure_deletion; if(sig == "bg") return fsan_no_tail_merging; if(sig == "bh") return fsan_undeletable; if(sig == "bi") return fsan_noatime_update; if(sig == "bj") return fsan_synchronous_directory; if(sig == "bk") return fsan_synchronous_update; if(sig == "bl") return fsan_top_of_dir_hierarchy; if(sig == "XX") // resevered for field extension if necessary in the future throw Erange("filesystem_specific_attribute_list::signature_to_nature", gettext("invalid FSA nature flag")); throw Erange("filesystem_specific_attribute_list::signature_to_nature", gettext("invalid FSA nature flag")); } /////////////////////////////////////////////////////////////////////////////////// fsa_bool::fsa_bool(generic_file & f, fsa_family fam, fsa_nature nat): filesystem_specific_attribute(f, fam, nat) { char ch; S_I lu = f.read(&ch, 1); if(lu == 1) { switch(ch) { case 'T': val = true; break; case 'F': val = false; break; default: throw Edata(gettext("Unexepected value for boolean FSA, data corruption may have occurred")); } } else throw Erange("fsa_bool::fsa_bool", string(gettext("Error while reading FSA: ")) + tools_strerror_r(errno)); } bool fsa_bool::equal_value_to(const filesystem_specific_attribute & ref) const { const fsa_bool *ptr = dynamic_cast(&ref); if(ptr != nullptr) return val == ptr->val; else return false; } /////////////////////////////////////////////////////////////////////////////////// fsa_infinint::fsa_infinint(generic_file & f, fsa_family fam, fsa_nature nat): filesystem_specific_attribute(f, fam, nat) { val.read(f); } string fsa_infinint::show_val() const { return deci(val).human(); } bool fsa_infinint::equal_value_to(const filesystem_specific_attribute & ref) const { const fsa_infinint *ptr = dynamic_cast(&ref); if(ptr != nullptr) return val == ptr->val; else return false; } /////////////////////////////////////////////////////////////////////////////////// fsa_time::fsa_time(generic_file & f, archive_version ver, fsa_family fam, fsa_nature nat): filesystem_specific_attribute(f, fam, nat) { val.read(f, ver); } string fsa_time::show_val() const { return tools_display_date(val); } bool fsa_time::equal_value_to(const filesystem_specific_attribute & ref) const { const fsa_time *ptr = dynamic_cast(&ref); if(ptr != nullptr) return val == ptr->val; else return false; } } // end of namespace dar-2.7.15/src/libdar/deci.hpp0000644000175000017500000000640714636066467012767 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file deci.hpp /// \brief manages the decimal representation of infinint /// \ingroup API #ifndef DECI_HPP #define DECI_HPP #include "../my_config.h" #include #include #include "storage.hpp" #include "infinint.hpp" namespace libdar { /// \addtogroup API /// @{ /// decimal class, convert infinint from and to decimal represention /// the class contains the decimal representation of an integer /// and can produce a readable string to display the corresponding /// integer it can also produce a computer value corresponding to /// the decimal value. In the other side, objects of this class can /// be built from a integer as well as from a string representing /// the decimals of an integer. class deci { public : /// constructor to build a "deci" object from a string representing decimals /// \note may throw Edeci exception if the given string does not correspond to a /// positive integer in decimal notation deci(std::string s); /// constructor to build a "deci" from an infinint deci(const infinint & x); /// copy constructor deci(const deci & ref) { copy_from(ref); }; /// move constructor deci(deci && ref) noexcept { decimales = nullptr; std::swap(decimales, ref.decimales); }; /// assignment operator deci & operator = (const deci & ref) { detruit(); copy_from(ref); return *this; }; /// assignment move operator deci & operator = (deci && ref) noexcept { std::swap(decimales, ref.decimales); return *this; }; /// destructor ~deci() { detruit(); }; /// this produce a infinint from the decimal stored in the current object infinint computer() const; /// this produce a string from the decimal stored in the current object std::string human() const; private : storage *decimales; void detruit(); void copy_from(const deci & ref); void reduce(); }; /// specific << operator to use infinint in std::ostream /// including "deci.hpp" let this operator available so you can /// display infinint with the << std::ostream operator as you can /// do for standard types. extern std::ostream & operator << (std::ostream & ref, const infinint & arg); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/crypto_asym.hpp0000644000175000017500000001057214636066467014432 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file crypto_asym.hpp /// \brief the asymetric cryptographical algoritms relying on gpgme /// \ingroup Private #ifndef CRYPTO_ASYM_HPP #define CRYPTO_ASYM_HPP extern "C" { #if HAVE_GPGME_H #include #endif } #include "../my_config.h" #include "generic_file.hpp" #include "mem_ui.hpp" #include "crypto.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// asymetric ciphering class crypto_asym : public mem_ui { public: /// general use constructor crypto_asym(const std::shared_ptr & ui) : mem_ui(ui) { build_context(); has_signatories = false; }; /// disabling copy constructor crypto_asym(const crypto_asym & ref) = delete; /// disabling move constuctor crypto_asym(crypto_asym && ref) = delete; /// disabling object assignment crypto_asym & operator = (const crypto_asym & ref) = delete; /// disabling move assignment operator crypto_asym & operator = (crypto_asym && ref) = delete; /// the destructor ~crypto_asym() { release_context(); }; /// defines the list of email *or keyid* which associated key will be used for signing void set_signatories(const std::vector & signatories); /// encrypt (and sign if signatures have been given using set_signatories) data for the given recipients /// \param[in] recipients_email list of email *or keyid* of recipient that will be able to read the encrypted data /// \param[in] clear where to read from clear data to be encrypted (the object must be readable) /// \param[out] ciphered where to write down encrypted data (the object must be writable) /// \note this assumes the GnuPG keyring has the public keys of the recipient listed void encrypt(const std::vector & recipients_email, generic_file & clear, generic_file & ciphered); /// un-cipher data /// \param[in] ciphered contains the encrypted data to decipher /// \param[out] clear resulting un-ciphered (thus clear) data (the object must be readable) /// \note this assumes the GnuPG keyring has an appropriated private key (the objet must be writable) void decrypt(generic_file & ciphered, generic_file & clear); /// after un-ciphering data retrieve the list of signature that were used beside encryption /// return a sorted list of signatories const std::list & verify() const { return signing_result; }; /// exposing to public visibility the protected method of mem_ui /// used to provide access to the user_interaction from the callback function /// required by gpgme_set_passphrase_cb(). user_interaction & get_ui() const { return mem_ui::get_ui(); }; private: bool has_signatories; std::list signing_result; #if GPGME_SUPPORT gpgme_ctx_t context; ///< GPGME context void release_context() { gpgme_release(context); }; void build_key_list(const std::vector & recipients_email, ///< list of email to find a key for gpgme_key_t * & ciphering_keys, ///< resulting nullptr terminated list of keys bool signatories ///< false if email key need encryption capability, true for signing ); void release_key_list(gpgme_key_t * & ciphering_keys); void fill_signing_result(); #else void release_context() {}; #endif void build_context(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/trivial_sar.hpp0000644000175000017500000002140214636066467014372 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file trivial_sar.hpp /// \brief the trivial_sar classes manages the slicing layer when single slice is used /// \ingroup Private #ifndef TRIVIAL_SAR_HPP #define TRIVIAL_SAR_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "generic_file.hpp" #include "integers.hpp" #include "entrepot.hpp" #include "contextual.hpp" #include "mem_ui.hpp" namespace libdar { // contextual is defined in generic_file module /// \addtogroup Private /// @{ /// "trivial sar" proposes the same interface a sar but does the work slightly differently using different constructors /// /// depending on the constructor used trivial sar can: /// - in write mode send the data to a arbitrary long slice (plain file) /// - in read mode let read a single slice from a named pipe /// - in write mode let write a single sliced archive to an anonymous pipe class trivial_sar : public generic_file, public contextual, protected mem_ui { public: /// constructor to build a new single sliced archive trivial_sar(const std::shared_ptr & dialog, ///< how to interact with the user gf_mode open_mode, ///< read_write or write_only are the only acceptable values const std::string & base_name, ///< archive basename to create const std::string & extension, ///< archive extension const entrepot & where, ///< where to store the archive const label & internal_nale, ///< tag common to all slices of the archive const label & data_name, ///< tag that follows the data when archive is dar_xform'ed const std::string & execute, ///< command line to execute at end of slice creation bool allow_over, ///< whether to allow overwriting bool warn_over, ///< whether to warn before overwriting bool force_permission, ///< whether to enforce slice permission or not U_I permission, ///< value of permission to use if permission enforcement is used hash_algo x_hash, ///< whether to build a hash of the slice, and which algo to use for that const infinint & min_digits, ///< is the minimum number of digits the slices number is stored with in the filename bool format_07_compatible ///< build a slice header backward compatible with 2.3.x ); /// constructor to read a (single sliced) archive from a pipe trivial_sar(const std::shared_ptr & dialog, ///< how to interact with the user const std::string & pipename, ///< if set to '-' the data are read from standard input, else the given file is expected to be named pipe to read data from bool lax ///< whether to be laxist or follow the normal and strict controlled procedure ); trivial_sar(const std::shared_ptr & dialog, ///< how to interact with the user int filedescriptor, ///< if set to '-' the data are read from standard input, else the given file is expected to be named pipe to read data from bool lax ///< whether to be laxist or follow the normal and strict controlled procedure ); /// constructor to write a (single sliced) archive to a anonymous pipe trivial_sar(const std::shared_ptr & dialog, ///< user interaction generic_file * f, ///< in case of exception the generic_file "f" is not released, this is the duty of the caller to do so, else (success), the object becomes owned by the trivial_sar and must not be released by the caller. const label & internal_name, ///< internal name ti use const label & data_name, ///< data name bool format_07_compatible, ///< whether we have to avoid creating a slice trailer const std::string & execute ///< command to execute after each slice ); /// copy constructor (disabled) trivial_sar(const trivial_sar & ref) = delete; /// move constructor trivial_sar(trivial_sar && ref) noexcept = delete; /// assignment operator (disabled) trivial_sar & operator = (const trivial_sar & ref) = delete; /// move operator trivial_sar & operator = (trivial_sar && ref) noexcept = delete; /// destructor ~trivial_sar(); virtual bool skippable(skippability direction, const infinint & amount) override { return reference->skippable(direction, amount); }; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override { if(is_terminated()) throw SRC_BUG; return reference->skip_to_eof(); }; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return reference->truncatable(offset + pos); }; virtual infinint get_position() const override { return cur_pos; }; // contextual inherited method virtual bool is_an_old_start_end_archive() const override { return old_sar; }; virtual const label & get_data_name() const override { return of_data_name; }; /// size of the slice header const infinint & get_slice_header_size() const { return offset; }; /// disable execution of user command when destroying the current object void disable_natural_destruction() { natural_destruction = false; }; /// enable back execution of user command when destroying the current object void enable_natural_destruction() { natural_destruction = true; }; protected: virtual void inherited_read_ahead(const infinint & amount) override { reference->read_ahead(amount); }; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override { reference->truncate(pos + offset); cur_pos = pos; }; virtual void inherited_sync_write() override { if(reference != nullptr) reference->sync_write(); }; virtual void inherited_flush_read() override { if(reference != nullptr) reference->flush_read(); }; virtual void inherited_terminate() override; private: generic_file *reference; ///< points to the underlying data, owned by "this" infinint offset; ///< offset to apply to get the first byte of data out of SAR headers infinint cur_pos; ///< current position as returned by get_position() infinint end_of_slice; ///< when end of slice/archive is met, there is an offset by 1 compared to the offset of reference. end_of_slice is set to 1 in that situation, else it is always equal to zero std::string hook; ///< command to execute after slice writing (not used in read-only mode) std::string base; ///< basename of the archive (used for string susbstitution in hook) std::string ext; ///< extension of the archive (used for string substitution in hook) label of_data_name; ///< archive's data name bool old_sar; ///< true if the read sar has an old header (format <= "07") or the to be written must keep a version 07 format. infinint min_digits; ///< minimum number of digits in slice name std::string hook_where; ///< what value to use for %p substitution in hook std::string base_url; ///< what value to use for %u substitution in hook bool natural_destruction; ///< whether user command is executed once the single sliced archive is completed (disable upon user interaction) void init(const label & internal_name); ///< write the slice header and set the offset field (write mode), or (read-mode), reads the slice header an set offset field void where_am_i(); }; /// return the name of a slice given the base_name, slice number and extension extern std::string sar_make_filename(const std::string & base_name, const infinint & num, const infinint & min_digits, const std::string & ext); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/thread_cancellation.cpp0000644000175000017500000002205414636066467016035 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif } #include "erreurs.hpp" #include "thread_cancellation.hpp" #include "tools.hpp" #define CRITICAL_START \ sigset_t Critical_section_mask_memory; \ tools_block_all_signals(Critical_section_mask_memory); \ pthread_mutex_lock(&access) #define CRITICAL_END pthread_mutex_unlock(&access); \ tools_set_back_blocked_signals(Critical_section_mask_memory) using namespace std; namespace libdar { // class static variables #if MUTEX_WORKS pthread_mutex_t thread_cancellation::access = PTHREAD_MUTEX_INITIALIZER; list thread_cancellation::info; list thread_cancellation::preborn; multimap thread_cancellation::thread_asso; #endif thread_cancellation::thread_cancellation() { #if MUTEX_WORKS bool bug = false; status.tid = pthread_self(); list::iterator ptr; CRITICAL_START; ptr = info.begin(); while(ptr != info.end() && *ptr != nullptr && (*ptr)->status.tid != status.tid) ptr++; if(ptr == info.end()) // first object in that thread { list::iterator it = preborn.begin(); while(it != preborn.end() && it->tid != status.tid) it++; if(it == preborn.end()) // no pending cancellation for that thread { status.block_delayed = false; status.immediate = true; status.cancellation = false; status.flag = 0; } else // pending cancellation for that thread { status = *it; preborn.erase(it); } } else // an object already exist for that thread if(*ptr == nullptr) // bug bug = true; else // an object already exists for that thread status = (*ptr)->status; if(!bug) info.push_back(this); CRITICAL_END; if(bug) throw SRC_BUG; #endif } thread_cancellation::~thread_cancellation() noexcept(false) { #if MUTEX_WORKS list::iterator ptr; bool bug = false; CRITICAL_START; ptr = info.begin(); while(ptr != info.end() && *ptr != this) ptr++; if(ptr == info.end()) bug = true; else if(*ptr == nullptr) bug = true; else { if((*ptr)->status.cancellation) // cancellation for that thread preborn.push_back((*ptr)->status); info.erase(ptr); } CRITICAL_END; if(bug) throw SRC_BUG; #endif } void thread_cancellation::check_self_cancellation() const { #if MUTEX_WORKS if(status.cancellation && (status.immediate || !status.block_delayed)) { (void)clear_pending_request(status.tid); // avoid other object of that thread to throw exception throw Ethread_cancel(status.immediate, status.flag); // we can throw the exception now } #endif } void thread_cancellation::block_delayed_cancellation(bool mode) { #if MUTEX_WORKS list::iterator ptr; // we update all object of the current thread CRITICAL_START; ptr = info.begin(); while(ptr != info.end()) { if(*ptr == nullptr) throw SRC_BUG; if((*ptr)->status.tid == status.tid) (*ptr)->status.block_delayed = mode; ptr++; } CRITICAL_END; if(status.block_delayed != mode) throw SRC_BUG; if(!mode) check_self_cancellation(); #endif } #if MUTEX_WORKS void thread_cancellation::cancel(pthread_t tid, bool x_immediate, U_64 x_flag) { bool found = false, bug = false, notused = false; multimap::iterator debut; multimap::iterator fin; CRITICAL_START; set_cancellation_in_info_for(tid, true, x_immediate, x_flag, found, notused, bug); if(!found && !bug) // no thread_cancellation object exist for that thread add_to_preborn(tid, x_immediate, x_flag); find_asso_tid_with(tid, debut, fin); while(debut != fin && !bug) { set_cancellation_in_info_for(debut->second, true, x_immediate, x_flag, found, notused, bug); if(!found && !bug) add_to_preborn(debut->second, x_immediate, x_flag); ++debut; } CRITICAL_END; if(bug) throw SRC_BUG; } #endif #if MUTEX_WORKS bool thread_cancellation::cancel_status(pthread_t tid) { bool ret, bug = false; list::iterator ptr; CRITICAL_START; ptr = info.begin(); while(ptr != info.end() && (*ptr) != nullptr && (*ptr)->status.tid != tid) ptr++; if(ptr == info.end()) { list::iterator it = preborn.begin(); while(it != preborn.end() && it->tid != tid) it++; if(it == preborn.end()) ret = false; else ret = it->cancellation; } else if(*ptr == nullptr) bug = true; else ret = (*ptr)->status.cancellation; CRITICAL_END; if(bug) throw SRC_BUG; return ret; } bool thread_cancellation::clear_pending_request(pthread_t tid) { bool ret = false, bug = false, found = false; multimap::iterator debut; multimap::iterator fin; CRITICAL_START; set_cancellation_in_info_for(tid, false, false, 0, found, ret, bug); if(!found && !bug) remove_from_preborn(tid, found, ret); find_asso_tid_with(tid, debut, fin); while(debut != fin && !bug) { set_cancellation_in_info_for(debut->second, false, false, 0, found, ret, bug); if(!found && !bug) remove_from_preborn(debut->second, found, ret); ++debut; } CRITICAL_END; if(bug) throw SRC_BUG; return ret; } void thread_cancellation::associate_tid_to_tid(pthread_t src, pthread_t dst) { CRITICAL_START; thread_asso.insert(pair(src,dst)); CRITICAL_END; } void thread_cancellation::remove_association_for_tid(pthread_t src) { CRITICAL_START; thread_asso.erase(src); CRITICAL_END; } void thread_cancellation::remove_association_targeted_at(pthread_t dst) { CRITICAL_START; multimap::iterator it = thread_asso.begin(); multimap::iterator next = it; while(it != thread_asso.end()) { if(it->second == dst) { next = it; ++next; thread_asso.erase(it); it = next; } else ++it; } CRITICAL_END; } void thread_cancellation::dead_thread(pthread_t tid) { bool found, prev; remove_association_for_tid(tid); remove_association_targeted_at(tid); remove_from_preborn(tid, found, prev); } void thread_cancellation::set_cancellation_in_info_for(pthread_t tid, bool cancel_status, bool x_immediate, U_64 x_flag, bool & found, bool & previous_val, bool & bug) { list::iterator ptr = info.begin(); found = false; bug = false; while(ptr != info.end() && !bug) { if(*ptr == nullptr) bug = true; else if((*ptr)->status.tid == tid) { found = true; (*ptr)->status.immediate = x_immediate; previous_val = (*ptr)->status.cancellation; (*ptr)->status.cancellation = cancel_status; (*ptr)->status.flag = x_flag; } ptr++; } } void thread_cancellation::add_to_preborn(pthread_t tid, bool x_immediate, U_64 x_flag) { list::iterator it = preborn.begin(); fields tmp; tmp.tid = tid; tmp.block_delayed = false; tmp.immediate = x_immediate; tmp.cancellation = true; tmp.flag = x_flag; while(it != preborn.end() && it->tid != tid) it++; if(it != preborn.end()) *it = tmp; else preborn.push_back(tmp); } void thread_cancellation::remove_from_preborn(pthread_t tid, bool & found, bool & prev) { list::iterator it = preborn.begin(); found = false; while(it != preborn.end()) { if(it->tid == tid) { found = true; prev = it->cancellation; preborn.erase(it); it = preborn.begin(); } else it++; } } void thread_cancellation::find_asso_tid_with(pthread_t tid, multimap::iterator & debut, multimap::iterator & fin) { pair< multimap::iterator, multimap::iterator > tmp = thread_asso.equal_range(tid); debut = tmp.first; fin = tmp.second; } #endif } // end of namespace dar-2.7.15/src/libdar/real_infinint.cpp0000644000175000017500000005251514636066467014700 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif } // end extern "C" #include "real_infinint.hpp" #include "erreurs.hpp" #include "tools.hpp" using namespace std; namespace libdar { infinint::endian infinint::used_endian = not_initialized; U_8 infinint::zeroed_field[ZEROED_SIZE]; infinint::infinint(proto_generic_file & x) { build_from_file(x); } void infinint::build_from_file(proto_generic_file & x) { unsigned char a; bool fin = false; infinint skip = 0; storage::iterator it; S_I lu; int_tools_bitfield bf; while(!fin) { lu = x.read((char *)&a, 1); if(lu <= 0) throw Erange("infinint::build_from_file(proto_generic_file)", gettext("Reached end of file before all data could be read")); if(a == 0) ++skip; else // end of size field { // computing the size to read U_I pos = 0; int_tools_expand_byte(a, bf); for(S_I i = 0; i < 8; ++i) pos += bf[i]; if(pos != 1) throw Erange("infinint::build_from_file(proto_generic_file)", gettext("Badly formed \"infinint\" or not supported format")); // more than 1 bit is set to 1 pos = 0; while(bf[pos] == 0) ++pos; pos += 1; // bf starts at zero, but bit zero means 1 TG of length skip *= 8; skip += pos; skip *= TG; try { field = new (nothrow) storage(x, skip); } catch(...) { field = nullptr; throw; } if(field != nullptr) { it = field->begin(); fin = true; } else throw Ememory("infinint::build_from_file(proto_generic_file)"); } } reduce(); // necessary to reduce due to TG storage } void infinint::dump(proto_generic_file & x) const { infinint width; infinint pos; unsigned char last_width; infinint justification; U_32 tmp; if(! is_valid()) throw SRC_BUG; if(*(field->begin()) == 0) const_cast(this)->reduce(); width = field->size(); // this is the informational field size in byte // TG is the width in TG, thus the number of bit that must have // the preamble euclide(width, TG, width, justification); if(!justification.is_zero()) // in case we need to add some bytes to have a width multiple of TG ++width; // we need then one more group to have a width multiple of TG euclide(width, 8, width, pos); if(pos.is_zero()) { --width; // division is exact, only last bit of the preambule is set last_width = 0x80 >> 7; // as we add the last byte separately width gets shorter by 1 byte } else // division non exact, the last_width (last byte), make the rounding { U_16 pos_s = 0; pos.unstack(pos_s); last_width = 0x80 >> (pos_s - 1); } // now we write the preamble except the last byte. All these are zeros. tmp = 0; width.unstack(tmp); do { while(tmp != 0) { if(tmp > ZEROED_SIZE) { x.write((char *)zeroed_field, ZEROED_SIZE); tmp -= ZEROED_SIZE; } else { x.write((char *)zeroed_field, tmp); tmp = 0; } } tmp = 0; width.unstack(tmp); } while(tmp > 0); // now we write the last byte of the preambule, which has only one bit set x.write((char *)&last_width, 1); // we need now to write some justification byte to have an informational field multiple of TG if(!justification.is_zero()) { U_16 tmp = 0; justification.unstack(tmp); tmp = TG - tmp; if(tmp > ZEROED_SIZE) throw SRC_BUG; else x.write((char *)zeroed_field, tmp); } // now we continue dumping the informational bytes : field->dump(x); } infinint & infinint::operator += (const infinint & arg) { if(! is_valid() || ! arg.is_valid()) throw SRC_BUG; // enlarge field to be able to receive the result of the operation make_at_least_as_wider_as(arg); // now processing the operation storage::iterator it_a = arg.field->rbegin(); storage::iterator it_res = field->rbegin(); U_I retenue = 0, somme; while(it_res != field->rend() && (it_a != arg.field->rend() || retenue != 0)) { somme = *it_res; if(it_a != arg.field->rend()) { somme += *it_a; --it_a; } somme += retenue; retenue = somme >> 8; somme &= 0xFF; *it_res = somme; --it_res; } if(retenue != 0) { field->insert_null_bytes_at_iterator(field->begin(), 1); (*field)[0] = retenue; } // reduce() is not necessary here, as the resulting filed is // not smaller than the one of the two infinint in presence. // resulting infinint is thus in canonical form (no leading zeros) return *this; } infinint & infinint::operator -= (const infinint & arg) { if(! is_valid() || ! arg.is_valid()) throw SRC_BUG; if(*this < arg) throw Erange("infinint::operator", gettext("Subtracting an \"infinint\" greater than the first, \"infinint\" cannot be negative")); // now processing the operation storage::iterator it_a = arg.field->rbegin(); storage::iterator it_res = field->rbegin(); U_I retenue = 0; S_I somme; U_I tmp; while(it_res != field->rend() && (it_a != arg.field->rend() || retenue != 0)) { somme = *it_res; if(it_a != arg.field->rend()) { somme -= *it_a; --it_a; } somme -= retenue; if(somme < 0) { somme = -somme; tmp = somme & 0xFF; retenue = somme >> 8; if(tmp != 0) { somme = 0x100 - tmp; ++retenue; } else somme = 0; } else retenue = 0; *it_res = somme; --it_res; } // the resulting infinint is most probably *NOT* in canonical form // to improve performance, since release 2.4.0, it is admitted that an infinint may not // be in canonical form. It will be "reduced()" to canonical form only when necessary // at the detriment of the space used during the gap. return *this; } infinint & infinint::operator *= (unsigned char arg) { if(!is_valid()) throw SRC_BUG; storage::iterator it = field->rbegin(); U_I produit, retenue = 0; // assuming U_I is larger than unsigned char while(it != field->rend()) { produit = (*it) * arg + retenue; retenue = 0; retenue = produit >> 8; produit = produit & 0xFF; *it = produit; --it; } if(retenue != 0) { field->insert_null_bytes_at_iterator(field->begin(), 1); (*field)[0] = retenue; } if(arg == 0) reduce(); // only necessary in that case // and it may worth it for big numbers so doing it // even if since release 2.4.0 it is allowed to keep an infinint // under non canonical form return *this; } infinint & infinint::operator *= (const infinint & arg) { infinint ret = 0; if(!is_valid() || !arg.is_valid()) throw SRC_BUG; storage::iterator it_t = field->begin(); while(it_t != field->end()) { ret <<= 8; // shift by one byte; ret += arg * (*it_t); ++it_t; } *this = ret; return *this; // copy constructor } infinint & infinint::operator &= (const infinint & arg) { if(! is_valid() || ! arg.is_valid()) throw SRC_BUG; make_at_least_as_wider_as(arg); storage::iterator it_a = arg.field->rbegin(); storage::iterator it_res = field->rbegin(); while(it_res != field->rend() && it_a != arg.field->rend()) { *it_res &= *it_a; --it_res; --it_a; } if(it_res != field->rend()) { while(it_res != field->rend()) // set upper bits to zero when arg is smaller than *this { *it_res = 0; --it_res; } reduce(); } return *this; } infinint & infinint::operator |= (const infinint & arg) { if(! is_valid() || ! arg.is_valid()) throw SRC_BUG; make_at_least_as_wider_as(arg); storage::iterator it_a = arg.field->rbegin(); storage::iterator it_res = field->rbegin(); while(it_res != field->rend() && it_a != arg.field->rend()) *it_res-- |= *it_a--; return *this; } infinint & infinint::operator ^= (const infinint & arg) { if(! is_valid() || ! arg.is_valid()) throw SRC_BUG; make_at_least_as_wider_as(arg); storage::iterator it_a = arg.field->rbegin(); storage::iterator it_res = field->rbegin(); while(it_res != field->rend() && it_a != arg.field->rend()) { *it_res ^= *it_a; --it_res; --it_a; } return *this; } infinint & infinint::operator >>= (U_32 bit) { if(! is_valid()) throw SRC_BUG; U_32 byte = bit/8; storage::iterator it = field->rbegin() - byte + 1; int_tools_bitfield bf; unsigned char mask, r1 = 0, r2 = 0; U_I shift_retenue; bit = bit % 8; shift_retenue = 8 - bit; if(byte >= field->size()) *this = 0; else { // shift right by "byte" bytes field->remove_bytes_at_iterator(it, byte); // shift right by "bit" bits if(bit != 0) { for(U_I i = 0; i < 8; ++i) bf[i] = i < shift_retenue ? 0 : 1; int_tools_contract_byte(bf, mask); it = field->begin(); while(it != field->end()) { r1 = *it & mask; r1 <<= shift_retenue; *it >>= bit; *it |= r2; r2 = r1; ++it; } } } return *this; } infinint & infinint::operator >>= (infinint bit) { if(! is_valid() || ! bit.is_valid()) throw SRC_BUG; U_32 delta_bit = 0; bit.unstack(delta_bit); do { *this >>= delta_bit; delta_bit = 0; bit.unstack(delta_bit); } while(delta_bit > 0); return *this; } infinint & infinint::operator <<= (U_32 bit) { if(! is_valid()) throw SRC_BUG; U_32 byte = bit/8; storage::iterator it = field->end(); if(this->is_zero()) return *this; bit %= 8; // bit gives now the remaining translation after the "byte" translation if(bit != 0) ++byte; // to prevent the MSB to be lost (in "out of space" ;-) ) // this is the "byte" translation field->insert_null_bytes_at_iterator(it, byte); if(bit != 0) { U_I shift_retenue, r1 = 0, r2 = 0; int_tools_bitfield bf; unsigned char mask; // and now the bit translation // we have shift left one byte in place of 'bit' bits, so we shift right // shift_retenue bits: shift_retenue = 8 - bit; it = field->begin(); // the mask for selecting the retenue for(U_I i = 0; i < 8; ++i) bf[i] = i < bit ? 0 : 1; int_tools_contract_byte(bf, mask); while(it != field->end()) { r1 = (*it) & mask; r1 <<= bit; *it >>= shift_retenue; *it |= r2; r2 = r1; ++it; } } return *this; } infinint & infinint::operator <<= (infinint bit) { U_32 delta_bit = 0; bit.unstack(delta_bit); do { *this <<= delta_bit; delta_bit = 0; bit.unstack(delta_bit); } while(delta_bit > 0); return *this; } unsigned char infinint::operator [] (const infinint & position) const { if(field == nullptr) throw SRC_BUG; if(position.is_zero()) { storage::iterator it = field->rbegin(); if(it != field->rend()) return *it; else return 0x00; } else { if(position < field->size()) return (*field)[field->size() - (position + 1)]; else return 0x00; } } bool infinint::is_zero() const { if(field == nullptr) throw SRC_BUG; storage::iterator it = field->begin(); while(it != field->end() && *it == 0) ++it; return it == field->end(); } S_I infinint::difference(const infinint & b) const { storage::iterator ita; storage::iterator itb; const infinint & a = *this; if(! a.is_valid() || ! b.is_valid()) throw SRC_BUG; // need to reduce object to their canonical form first and if not already in canonical form if(*(a.field->begin()) == 0) const_cast(this)->reduce(); // reducing "this" which a is a reference to, thus reducing "a" if(*(b.field->begin()) == 0) const_cast(b).reduce(); if(*a.field < *b.field) // field is shorter for this than for ref and object have been reduced "reduced", thus a < b return -1; else if(*a.field > *b.field) return +1; else // *a.field == *b.field { ita = a.field->begin(); itb = b.field->begin(); while(ita != a.field->end() && itb != b.field->end() && *ita == *itb) { ++ita; ++itb; } if(ita == a.field->end() && itb == b.field->end()) return 0; if(itb == b.field->end()) return +1; // b can't be greater than a, at most it can be equal to it if(ita == a.field->end()) return -1; // because itb != b.field->end(); return *ita - *itb; } } bool infinint::is_valid() const noexcept { return field != nullptr; } void infinint::reduce() { static const U_I max_a_time = ~ (U_I)(0); // this is the argument type of remove_bytes_at_iterator U_I count = 0; storage::iterator it = field->begin(); do { while(it != field->end() && (*it) == 0 && count < max_a_time) { ++it; ++count; } if(it == field->end()) // all zeros { if(count == 0) // empty storage; field->insert_null_bytes_at_iterator(field->begin(), 1); // field width is at least one byte else if(count > 1) field->remove_bytes_at_iterator(field->begin(), count - 1); // cannot remove count because we reached the end of field thus field only contains zeros, and // we must keep field at least one byte width. // else count == 1 and nothing has to be done } else { if(count > 0) field->remove_bytes_at_iterator(field->begin(), count); count = 0; it = field->begin(); } } while(it != field->end() && (*it) == 0); } void infinint::copy_from(const infinint & ref) { if(ref.is_valid()) { field = new (nothrow) storage(*(ref.field)); if(field == nullptr) throw Ememory("infinint::copy_from"); } else throw SRC_BUG; } void infinint::detruit() { if(field != nullptr) { delete field; field = nullptr; } } void infinint::make_at_least_as_wider_as(const infinint & ref) { if(! is_valid() || ! ref.is_valid()) throw SRC_BUG; field->insert_as_much_as_necessary_const_byte_to_be_as_wider_as(*ref.field, field->begin(), 0x00); } void infinint::setup_endian() { if(integers_system_is_big_endian()) used_endian = big_endian; else used_endian = little_endian; (void)memset(zeroed_field, 0, ZEROED_SIZE); } bool infinint::is_system_big_endian() { if(used_endian == not_initialized) setup_endian(); switch(used_endian) { case big_endian: return true; case little_endian: return false; case not_initialized: throw SRC_BUG; default: throw SRC_BUG; } } /////////////////////////////////////////////////////////////////////// ///////////////// friends and not friends of infinint ///////////////// /////////////////////////////////////////////////////////////////////// infinint operator + (const infinint & a, const infinint & b) { infinint ret = a; ret += b; return ret; } infinint operator - (const infinint & a, const infinint & b) { infinint ret = a; ret -= b; return ret; } infinint operator * (const infinint & a, const infinint & b) { infinint ret = a; ret *= b; return ret; } infinint operator * (const infinint &a, const unsigned char b) { infinint ret = a; ret *= b; return ret; } infinint operator * (const unsigned char a, const infinint &b) { infinint ret = b; ret *= a; return ret; } infinint operator / (const infinint & a, const infinint & b) { infinint q, r; euclide(a, b, q, r); return q; } infinint operator % (const infinint & a, const infinint & b) { infinint q, r; euclide(a, b, q, r); return r; } infinint operator & (const infinint & a, const infinint & bit) { infinint ret = a; ret &= bit; return ret; } infinint operator | (const infinint & a, const infinint & bit) { infinint ret = a; ret |= bit; return ret; } infinint operator ^ (const infinint & a, const infinint & bit) { infinint ret = a; ret ^= bit; return ret; } infinint operator >> (const infinint & a, U_32 bit) { infinint ret = a; ret >>= bit; return ret; } infinint operator >> (const infinint & a, const infinint & bit) { infinint ret = a; ret >>= bit; return ret; } infinint operator << (const infinint & a, U_32 bit) { infinint ret = a; ret <<= bit; return ret; } infinint operator << (const infinint & a, const infinint & bit) { infinint ret = a; ret <<= bit; return ret; } void euclide(infinint a, const infinint &b, infinint &q, infinint &r) { if(b.is_zero()) throw Einfinint("infinint.cpp : euclide", gettext("Division by zero")); // division by zero if(a < b) { q = 0; r = a; return; } // need to reduce a and b first if(*(a.field->begin()) == 0) a.reduce(); r = b; if(*(r.field->begin()) == 0) r.reduce(); while(*a.field >= *r.field) r <<= 8; // one byte q = 0; while(b < r) { r >>= 8; // one byte; q <<= 8; // one byte; while(r <= a) { a -= r; ++q; } } r = a; } } // end of namespace dar-2.7.15/src/libdar/fichier_libcurl.cpp0000644000175000017500000005312614640016434015163 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "tools.hpp" #include "erreurs.hpp" #include "fichier_libcurl.hpp" using namespace std; namespace libdar { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) fichier_libcurl::fichier_libcurl(const shared_ptr & dialog, const std::string & chemin, mycurl_protocol proto, const shared_ptr & handle, gf_mode m, U_I waiting, bool force_permission, U_I permission, bool erase): fichier_global(dialog, m), end_data_mode(false), sub_is_dying(false), sync_write_asked(false), weof(false), ehandle(handle), metadatamode(false), current_offset(0), has_maxpos(false), maxpos(0), append_write(!erase), meta_inbuf(0), wait_delay(waiting), interthread(10, tampon_size), synchronize(2), x_proto(proto) { try { if(!ehandle) throw SRC_BUG; // setting x_ref_handle to carry all options that will always be present for this object ehandle->setopt(CURLOPT_URL, chemin); switch(get_mode()) { case gf_read_only: ehandle->setopt(CURLOPT_WRITEDATA, (void *)this); break; case gf_write_only: ehandle->setopt(CURLOPT_READDATA, (void *)this); ehandle->setopt(CURLOPT_UPLOAD, 1L); break; case gf_read_write: throw Efeature("read-write mode for fichier libcurl"); default: throw SRC_BUG; } switch_to_metadata(true); if(append_write && m != gf_read_only) current_offset = get_size(); } catch(...) { detruit(); throw; } } void fichier_libcurl::change_permission(U_I perm) { mycurl_slist headers; string order = tools_printf("site CHMOD %o", perm); switch_to_metadata(true); try { headers.append(order); ehandle->setopt(CURLOPT_QUOTE, headers); ehandle->setopt(CURLOPT_NOBODY, (long)1); try { ehandle->apply(get_pointer(), wait_delay); } catch(...) { ehandle->setopt_default(CURLOPT_QUOTE); ehandle->setopt_default(CURLOPT_NOBODY); throw; } ehandle->setopt_default(CURLOPT_QUOTE); ehandle->setopt_default(CURLOPT_NOBODY); } catch(Egeneric & e) { e.prepend_message("Error while changing file permission on remote repository"); throw; } } infinint fichier_libcurl::get_size() const { curl_off_t filesize; fichier_libcurl *me = const_cast(this); if(me == nullptr) throw SRC_BUG; if(!has_maxpos) { try { me->switch_to_metadata(true); me->ehandle->setopt(CURLOPT_NOBODY, (long)1); try { me->ehandle->apply(get_pointer(), wait_delay); me->ehandle->getinfo(CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &filesize); if(filesize == -1) // file does not exist (or filesize is not known) filesize = 0; me->maxpos = infinint(filesize); me->has_maxpos = true; } catch(...) { me->ehandle->setopt_default(CURLOPT_NOBODY); throw; } me->ehandle->setopt_default(CURLOPT_NOBODY); } catch(Egeneric & e) { e.prepend_message("Error while reading file size on a remote repository: "); throw; } } return maxpos; } bool fichier_libcurl::skippable(skippability direction, const infinint & amount) { if(get_mode() == gf_read_only) { switch(direction) { case skip_backward: return amount <= current_offset; case skip_forward: if(!has_maxpos) (void)get_size(); if(!has_maxpos) throw SRC_BUG; return current_offset + amount < maxpos; default: throw SRC_BUG; } } else return false; } bool fichier_libcurl::skip(const infinint & pos) { if(pos == current_offset) return true; switch(get_mode()) { case gf_read_only: switch_to_metadata(true); // necessary to stop current subthread and change easy_handle offset current_offset = pos; flush_read(); break; case gf_write_only: throw Erange("fichier_libcurl::skip", string(gettext("libcurl does not allow skipping in write mode"))); case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } return true; } bool fichier_libcurl::skip_to_eof() { (void)get_size(); if(!has_maxpos) throw SRC_BUG; // get_size() sould either throw an exception or set maxpos if(get_mode() == gf_write_only) return true; else return skip(maxpos); } bool fichier_libcurl::skip_relative(S_I x) { if(x >= 0) { infinint tmp(x); tmp += current_offset; return skip(tmp); } else { infinint tmp(-x); if(tmp > current_offset) { skip(0); return false; } else { tmp = current_offset - tmp; return skip(tmp); } } } void fichier_libcurl::inherited_read_ahead(const infinint & amount) { relaunch_thread(amount); } void fichier_libcurl::inherited_truncate(const infinint & pos) { if(pos != get_position()) throw Erange("fichier_libcurl::inherited_truncate", string(gettext("libcurl does not allow truncating at a given position while uploading files"))); } void fichier_libcurl::inherited_sync_write() { char* ptr = nullptr; unsigned int size; // we must return only once all interthread data have been consumed by the subthread switch(get_mode()) { case gf_write_only: sync_write_asked = true; if(is_running() && ! sub_is_dying) { interthread.get_block_to_feed(ptr, size); interthread.feed(ptr, 0); // this triggers sync_write when sync_write_asked is set synchronize.wait(); } else sync_write_asked = false; break; case gf_read_only: throw SRC_BUG; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } } void fichier_libcurl::inherited_flush_read() { switch_to_metadata(true); interthread.reset(); } void fichier_libcurl::inherited_terminate() { switch(get_mode()) { case gf_write_only: inherited_sync_write(); switch_to_metadata(true); break; case gf_read_only: switch_to_metadata(true); break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } } U_I fichier_libcurl::fichier_global_inherited_write(const char *a, U_I size) { U_I wrote = 0; bool full = false; char *ptr; unsigned int ptr_size; switch_to_metadata(false); while(wrote < size && !full) { try { if(!is_running() || sub_is_dying) { join(); throw SRC_BUG; // inherited_run() should throw an exception // as this is not a normal condition: // we have not yet finished writing // data and child thread has already ended } } catch(Edata & e) { // remote disk is full full = true; } if(!full) { U_I toadd = size - wrote; interthread.get_block_to_feed(ptr, ptr_size); if(toadd <= ptr_size) { memcpy(ptr, a + wrote, toadd); interthread.feed(ptr, toadd); wrote = size; } else { memcpy(ptr, a + wrote, ptr_size); interthread.feed(ptr, ptr_size); wrote += ptr_size; } } } current_offset += wrote; if(current_offset > 0) append_write = true; // we can now ignore the request to erase data // and we now need to swap in write append mode if(maxpos < current_offset) { maxpos = current_offset; has_maxpos = true; } return wrote; } bool fichier_libcurl::fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) { char *ptr; unsigned int ptr_size; U_I room; U_I delta; bool maybe_eof = false; set_subthread(size); read = 0; do { delta = 0; while(read + delta < size && (!sub_is_dying || interthread.is_not_empty())) { interthread.fetch(ptr, ptr_size); room = size - read - delta; if(room >= ptr_size) { memcpy(a + read + delta, ptr, ptr_size); interthread.fetch_recycle(ptr); delta += ptr_size; } else { memcpy(a + read + delta, ptr, room); delta += room; ptr_size -= room; memmove(ptr, ptr + room, ptr_size); interthread.fetch_push_back(ptr, ptr_size); } } current_offset += delta; read += delta; if(read < size // we requested more data than what we got so far && (!has_maxpos // we don't know where is EOF || current_offset < maxpos) // or we have not yet reached EOF && !maybe_eof) // avoid looping endelessly { maybe_eof = (delta == 0); U_I remaining = size - read; // if interthread is empty and thread has not been launched at least once // we can only now switch to data mode because current_offset is now correct. // This will (re-)launch the thread that should fill interthread pipe with data set_subthread(remaining); size = read + remaining; } } while(read < size && (is_running() || interthread.is_not_empty())); return true; } void fichier_libcurl::inherited_run() { try { // parent thread is still suspended shared_ptr thread_ui = get_pointer(); infinint local_network_block = network_block; // set before unlocking parent thread try { if(!thread_ui) throw Ememory("fichier_libcurl::inherited_run"); subthread_cur_offset = current_offset; } catch(...) { initialize_subthread(); throw; } // after next call, the parent thread will be running initialize_subthread(); if(local_network_block.is_zero()) // network_block may be non null only in read-only mode { do { ehandle->apply(thread_ui, wait_delay, end_data_mode); } while(still_data_to_write()); } else // reading by block to avoid having interrupting libcurl { do { subthread_net_offset = 0; // keeps trace of the amount of bytes sent to main thread by callback set_range(subthread_cur_offset, local_network_block); try { ehandle->apply(thread_ui, wait_delay); subthread_cur_offset += subthread_net_offset; if(local_network_block < subthread_net_offset) throw SRC_BUG; // we acquired more data from libcurl than expected! local_network_block -= subthread_net_offset; } catch(...) { unset_range(); throw; } unset_range(); } while(!subthread_net_offset.is_zero() // we just grabbed some data in this ending cycle (not reached eof) && !end_data_mode // the current thread has not been asked to stop && !local_network_block.is_zero()); // whe still not have gathered all the requested data } } catch(...) { finalize_subthread(); throw; } finalize_subthread(); } void fichier_libcurl::initialize_subthread() { sub_is_dying = false; weof = false; synchronize.wait(); // release calling thread as we, as child thread, do now exist } void fichier_libcurl::finalize_subthread() { sub_is_dying = true; if(!end_data_mode) // natural death, main thread has not required our death { char *ptr; unsigned int ptr_size; switch(get_mode()) { case gf_write_only: // making room in the pile to toggle main thread if // it was suspended waiting for a block to feed interthread.fetch(ptr, ptr_size); interthread.fetch_recycle(ptr); break; case gf_read_only: // sending a zero length block to toggle main thread // if it was suspended waiting for a block to fetch interthread.get_block_to_feed(ptr, ptr_size); interthread.feed(ptr, 0); // means eof to main thread break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } } } void fichier_libcurl::set_range(const infinint & begin, const infinint & range_size) { infinint end_range = begin + range_size - 1; string range = tools_printf("%i-%i", &begin, &end_range); // setting the block size if necessary ehandle->setopt(CURLOPT_RANGE, range); } void fichier_libcurl::unset_range() { ehandle->setopt_default(CURLOPT_RANGE); } void fichier_libcurl::switch_to_metadata(bool mode) { if(mode == metadatamode) return; if(!mode) // data mode { infinint resume; curl_off_t cur_pos = 0; long do_append; switch(get_mode()) { case gf_read_only: ehandle->setopt(CURLOPT_WRITEFUNCTION, (void*)write_data_callback); if(network_block.is_zero()) { // setting the offset of the next byte to read / write resume = current_offset; resume.unstack(cur_pos); if(!resume.is_zero()) throw Erange("fichier_libcurl::switch_to_metadata", gettext("Integer too large for libcurl, cannot skip at the requested offset in the remote repository")); ehandle->setopt(CURLOPT_RESUME_FROM_LARGE, cur_pos); } // else (network_block != 0) the subthread will make use of range // this parameter is set back to its default in stop_thread() break; case gf_write_only: ehandle->setopt(CURLOPT_READFUNCTION, (void*)read_data_callback); // setting the offset of the next byte to read / write do_append = (append_write ? 1 : 0); ehandle->setopt(CURLOPT_APPEND, do_append); // should also set the CURLOPT_INFILESIZE_LARGE option but file size is not known at this time break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } run_thread(); } else // metadata mode { stop_thread(); meta_inbuf = 0; // we don't care existing metadata remaining in transfer switch(get_mode()) { case gf_read_only: ehandle->setopt(CURLOPT_WRITEFUNCTION, (void*)write_meta_callback); break; case gf_write_only: ehandle->setopt(CURLOPT_READFUNCTION, (void*)read_meta_callback); break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } } metadatamode = mode; } void fichier_libcurl::detruit() { try { terminate(); } catch(...) { // ignore all errors } } void fichier_libcurl::run_thread() { if(is_running()) throw SRC_BUG; if(interthread.is_not_empty()) { char *ptr; unsigned int ptr_size; bool bug = false; // the interthread may keep // a single empty block pending // to be fetched. interthread.fetch(ptr, ptr_size); if(ptr_size != 0) bug = true; interthread.fetch_recycle(ptr); if(bug) throw SRC_BUG; // now interthread should be empty if(interthread.is_not_empty()) bug = true; if(bug) throw SRC_BUG; // interthread should have been purged when // previous thread had ended } end_data_mode = false; run(); synchronize.wait(); // waiting for child thread to be ready } void fichier_libcurl::stop_thread() { if(is_running()) { char *ptr = nullptr; unsigned int ptr_size; end_data_mode = true; switch(get_mode()) { case gf_write_only: interthread.get_block_to_feed(ptr, ptr_size); interthread.feed(ptr, 0); // trigger the thread if it was waiting for data from interthread break; case gf_read_only: if(interthread.is_full()) { interthread.fetch(ptr, ptr_size); interthread.fetch_recycle(ptr); // trigger the thread if it was waiting for a free block to fill } break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } } join(); ehandle->setopt_default(CURLOPT_RESUME_FROM_LARGE); } void fichier_libcurl::relaunch_thread(const infinint & block_size) { if(metadatamode) { if(x_proto == proto_ftp) network_block = 0; else network_block = block_size; switch_to_metadata(false); } else { if(sub_is_dying) { stop_thread(); if(x_proto == proto_ftp) network_block = 0; else network_block = block_size; run_thread(); } // else thread is still running so // we cannot change the network_block size } } size_t fichier_libcurl::write_data_callback(char *buffer, size_t size, size_t nmemb, void *userp) { size_t remain = size * nmemb; size_t lu = 0; fichier_libcurl *me = (fichier_libcurl *)(userp); char *ptr; unsigned int ptr_size; if(me == nullptr) throw SRC_BUG; while(!me->end_data_mode && remain > 0) { me->interthread.get_block_to_feed(ptr, ptr_size); if(remain <= ptr_size) { memcpy(ptr, buffer + lu, remain); me->interthread.feed(ptr, remain); lu += remain; remain = 0; } else { memcpy(ptr, buffer + lu, ptr_size); me->interthread.feed(ptr, ptr_size); remain -= ptr_size; lu += ptr_size; } } if(me->network_block > 0) me->subthread_net_offset += lu; if(me->end_data_mode) { if(me->network_block == 0) { if(remain > 0) // not all data could be sent to main thread lu = 0; // to force easy_perform() that called us, to return } else { if(remain > 0) throw SRC_BUG; // main thread should not ask us to stop // until we have provided all the requested data } } return lu; } size_t fichier_libcurl::read_data_callback(char *bufptr, size_t size, size_t nitems, void *userp) { size_t ret; size_t room = size * nitems; fichier_libcurl *me = (fichier_libcurl *)(userp); char *ptr; unsigned int ptr_size; bool just_synced = false; if(me == nullptr) throw SRC_BUG; bool fetch_block = !me->weof || me->sync_write_asked; // with libcurl 7.74.0 and below, returning zero // bytes was properly addressed by libcurl as an EOF // and no more call to read_data_callback was "performed". // But with libcurl 7.88.1 read_data_callback() was called // again at least once after that, which lead fichier_libcurl // main thread hanging forever for curl_perform to end // because read_data_callback() was pending to receive // data from interthread pipe. To avoid that having the ending // of the subthread involving external software (libcurl) which // somehow changed without notice nor documentation change, we // add the weof boolean variable to avoid fetching from interthread // in that context. do { just_synced = false; if(fetch_block) me->interthread.fetch(ptr, ptr_size); else // emulate an the fetching of a zero length block { ptr_size = 0; ptr = bufptr; // we will copy 0 bytes from bufptr to bufptr } // note: if ptr_size is zero // libcurl will assume EOF and stop // the transfer process. if(me->sync_write_asked && ptr_size == 0) { me->sync_write_asked = false; if(fetch_block) me->interthread.fetch_recycle(ptr); me->synchronize.wait(); just_synced = true; } } while(just_synced); if(ptr_size <= room) { memcpy(bufptr, ptr, ptr_size); if(fetch_block) me->interthread.fetch_recycle(ptr); ret = ptr_size; } else { memcpy(bufptr, ptr, room); ptr_size -= room; memmove(ptr, ptr + room, ptr_size); if(fetch_block) me->interthread.fetch_push_back(ptr, ptr_size); ret = room; } if(fetch_block && ptr_size == 0 && !(me->sync_write_asked)) me->weof = true; return ret; } size_t fichier_libcurl::write_meta_callback(char *buffer, size_t size, size_t nmemb, void *userp) { return size * nmemb; } size_t fichier_libcurl::read_meta_callback(char *bufptr, size_t size, size_t nitems, void *userp) { return 0; } void fichier_libcurl::set_subthread(U_I & needed_bytes) { if(interthread.is_empty()) { // cannot switch to data mode if some data are // in transit because current_offset would be // wrongly positionned in the requested to libcurl if(metadatamode) { if(x_proto == proto_ftp) network_block = 0; // because reading by block lead control session to // be reset when ftp is used, leading a huge amount // of connection an FTP server might see as DoS atempt else { if(has_maxpos && maxpos <= current_offset + needed_bytes) { infinint tmp = maxpos - current_offset; // this sets size the value of tmp: needed_bytes = 0; tmp.unstack(needed_bytes); if(!tmp.is_zero()) throw SRC_BUG; network_block = 0; } else network_block = needed_bytes; } switch_to_metadata(false); } else { if(sub_is_dying) relaunch_thread(needed_bytes); } } } bool fichier_libcurl::still_data_to_write() { if(get_mode() == gf_write_only) { if(interthread.is_empty()) return false; else { char *ptr; unsigned int size; interthread.fetch(ptr, size); if(size == 0) { interthread.fetch_recycle(ptr); return false; } else { interthread.fetch_push_back(ptr, size); return true; } } } else return false; } #endif } // end of namespace dar-2.7.15/src/libdar/get_version.cpp0000644000175000017500000002074214636066467014400 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_SYS_TYPES_H #include #endif #if HAVE_UNISTD_H #include #endif #if LIBLZO2_AVAILABLE #if HAVE_LZO_LZO1X_H #include #endif #endif #if HAVE_STDLIB_H #include #endif #if HAVE_GCRYPT_H #ifndef GCRYPT_NO_DEPRECATED #define GCRYPT_NO_DEPRECATED #endif #include #endif #if LIBCURL_AVAILABLE #ifdef HAVE_CURL_CURL_H #include #endif #endif #if HAVE_TIME_H #include #endif #if HAVE_GPGME_H #include #endif } // end extern "C" #include #include #include "get_version.hpp" #include "erreurs.hpp" #include "nls_swap.hpp" #include "tools.hpp" #include "thread_cancellation.hpp" #include "mycurl_easyhandle_node.hpp" #ifdef LIBTHREADAR_AVAILABLE #include "parallel_tronconneuse.hpp" #endif using namespace std; namespace libdar { static void libdar_init(bool init_libgcrypt_if_not_done, // drives the "libdar_initialized" variable bool init_gpgme); // whether to initialize gpgme static bool libdar_initialized = false; //< static variable modified once during the first get_version call #ifdef CRYPTO_AVAILABLE static bool libdar_initialized_gcrypt = false; //< to record whether libdar did initialized libgcrypt #endif void get_version(U_I & major, U_I & minor, bool init_libgcrypt) { NLS_SWAP_IN; major = LIBDAR_COMPILE_TIME_MAJOR; minor = LIBDAR_COMPILE_TIME_MINOR; libdar_init(init_libgcrypt, true); NLS_SWAP_OUT; } void get_version(bool init_libgcrypt) { U_I maj, min, med; get_version(maj, min, med, init_libgcrypt); } void get_version(U_I & major, U_I & medium, U_I & minor, bool init_libgcrypt) { NLS_SWAP_IN; major = LIBDAR_COMPILE_TIME_MAJOR; medium = LIBDAR_COMPILE_TIME_MEDIUM; minor = LIBDAR_COMPILE_TIME_MINOR; libdar_init(init_libgcrypt, true); NLS_SWAP_OUT; } void get_version(U_I & major, U_I & medium, U_I & minor, bool init_libgcrypt, bool init_gpgme) { NLS_SWAP_IN; major = LIBDAR_COMPILE_TIME_MAJOR; medium = LIBDAR_COMPILE_TIME_MEDIUM; minor = LIBDAR_COMPILE_TIME_MINOR; libdar_init(init_libgcrypt, init_gpgme); NLS_SWAP_OUT; } static void libdar_init(bool init_libgcrypt_if_not_done, bool init_gpgme) { if(!libdar_initialized) { // locale for gettext if(string(DAR_LOCALEDIR) != string("")) if(bindtextdomain(PACKAGE, DAR_LOCALEDIR) == nullptr) throw Erange("", "Cannot open the translated messages directory, native language support will not work"); // pseudo random generator srand(::time(nullptr)+getpid()+getppid()); // initializing LIBLZO2 #if HAVE_LIBLZO2 if(lzo_init() != LZO_E_OK) throw Erange("libdar_init_thread_safe", gettext("Initialization problem for liblzo2 library")); #endif // initializing libgcrypt #ifdef CRYPTO_AVAILABLE if(!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { if(init_libgcrypt_if_not_done) { gcry_error_t err; // no multi-thread support activated for gcrypt // this must be done from the application as stated // by the libgcrypt documentation if(!gcry_check_version(MIN_VERSION_GCRYPT)) throw Erange("libdar_init_libgcrypt", tools_printf(gettext("Too old version for libgcrypt, minimum required version is %s"), MIN_VERSION_GCRYPT)); // initializing default sized secured memory for libgcrypt (void)gcry_control(GCRYCTL_INIT_SECMEM, 262144); // if secured memory could not be allocated, further request of secured memory will fail // and a warning will show at that time (we cannot send a warning (just failure notice) at that time). err = gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); if(err != GPG_ERR_NO_ERROR) throw Erange("libdar_init",tools_printf(gettext("Error while telling libgcrypt that initialization is finished: %s/%s"), gcry_strsource(err),gcry_strerror(err))); libdar_initialized_gcrypt = true; } else throw Erange("libdar_init_libgcrypt", gettext("libgcrypt not initialized and libdar not allowed to do so")); } else if(!gcry_check_version(MIN_VERSION_GCRYPT)) throw Erange("libdar_init_libgcrypt", tools_printf(gettext("Too old version for libgcrypt, minimum required version is %s"), MIN_VERSION_GCRYPT)); #endif // initializing gpgme #if GPGME_SUPPORT if(init_gpgme) { if(gpgme_check_version(GPGME_MIN_VERSION) == nullptr) { string tmp = "GPGME_SUPPORT"; throw Erange("libdar_init_gpgme", tools_printf(gettext("GPGME version requirement is not satisfied, requires version > %s"), tmp.c_str())); } if(gpgme_err_code(gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP)) != GPG_ERR_NO_ERROR) throw Erange("libdar_init_gpgme", tools_printf(gettext("GPGME engine not available: %s"), gpgme_get_protocol_name(GPGME_PROTOCOL_OpenPGP))); } #endif // initializing libcurl #if LIBCURL_AVAILABLE CURLcode curlret = curl_global_init(CURL_GLOBAL_ALL); if(curlret != 0) { const char *msg = curl_easy_strerror(curlret); throw Erange("libdar_init_libcurl", tools_printf(gettext("libcurl initialization failed: %s"), msg)); } const curl_version_info_data *cvers = curl_version_info(CURLVERSION_FOURTH); if(cvers->age < CURLVERSION_FOURTH) throw Erange("libdar_init_libcurl", tools_printf(gettext("libcurl initialization failed: %s"), "libcurl version not available")); if(cvers->version_num < 0x072600) throw Erange("libdar_init_libcurl", tools_printf(gettext("libcurl initialization failed: %s"), "libcurl version is too old")); // now that libgcrypt is eventually initialized (as well as libcurl, but it does not matter here) // we can initialize the default values for mycurl_easyhandle_node::defaults static field mycurl_easyhandle_node::init_defaults(); #endif // checking libthreadar version #if LIBTHREADAR_AVAILABLE unsigned int maj, med, min; libthreadar::get_version(maj, med, min); if((signed int)maj != atoi(EXPECTED_MAJ_VERSION_THREADAR) || (signed int)med < atoi(MIN_MED_VERSION_THREADAR) || ((signed int)med == atoi(MIN_MED_VERSION_THREADAR) && (signed int)min < atoi(MIN_MIN_VERSION_THREADAR))) throw Erange("libdar_init_libthreadar", tools_printf(gettext("libthreader version %d.%d.%d is too old, need version %s.%s.%s or more recent"), maj,med,min, EXPECTED_MAJ_VERSION_THREADAR, MIN_MED_VERSION_THREADAR, MIN_MIN_VERSION_THREADAR)); #endif tools_init(); // so now libdar is ready for use! libdar_initialized = true; } } extern void close_and_clean() { #ifdef LIBCURL_AVAILABLE // mycurl_easyhandle_node::efaults static field, may contain secu_string that rely on libgcrypt secured memory mycurl_easyhandle_node::release_defaults(); // it must thus be released before released libgcrypt below #endif #ifdef CRYPTO_AVAILABLE if(libdar_initialized_gcrypt) gcry_control(GCRYCTL_TERM_SECMEM, 0); // by precaution if not already done by libgcrypt itself #endif #if LIBCURL_AVAILABLE curl_global_cleanup(); #endif tools_end(); } #if MUTEX_WORKS void cancel_thread(pthread_t tid, bool immediate, U_64 flag) { thread_cancellation::cancel(tid, immediate, flag); } bool cancel_status(pthread_t tid) { return thread_cancellation::cancel_status(tid); } bool cancel_clear(pthread_t tid) { return thread_cancellation::clear_pending_request(tid); } U_I get_thread_count() { return thread_cancellation::count(); } #endif } // end of namespace dar-2.7.15/src/libdar/archive_num.hpp0000644000175000017500000000510314636066467014353 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive_num.hpp /// \brief class storing the position of an archive inside a database /// \ingroup API #ifndef ARCHIVE_NUM_HPP #define ARCHIVE_NUM_HPP #include "../my_config.h" #include "integers.hpp" #include "erreurs.hpp" namespace libdar { /// \addtogroup API /// @{ // no need to dig into this class from the API class generic_file; /// class archive_num stores the position of an archive inside a dar_manager database class archive_num { public: archive_num(U_16 arg = 0) { set(arg); }; archive_num(const archive_num & ref) = default; archive_num(archive_num && ref) noexcept = default; archive_num & operator = (const archive_num & ref) = default; archive_num & operator = (archive_num && ref) = default; ~archive_num() = default; /// this operator makes an object of that class convertible to an 16 bits integer /// \note this is the only call you should need, just use an archive_num /// implictely of explicitely as an integer operator U_16() const { return val; }; U_16 operator = (U_16 arg) { set(arg); return arg; }; archive_num & operator++() { set(val+1); return *this; }; // no need of order operator (<, <=, >, >=, ==, !=) // thanks to the implicit conversion to U_16 defined // above void read_from_file(generic_file &f); void write_to_file(generic_file &f) const; private: static constexpr U_I val_size = sizeof(U_16); static constexpr U_I MAX = 65534; U_16 val; inline void set(U_16 arg) { if(arg >= MAX) throw SRC_BUG; val = arg; } }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/etage.hpp0000644000175000017500000000477214636067146013146 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file etage.hpp /// \brief definition of the etage structure is done here /// \ingroup Private #ifndef ETAGE_HPP #define ETAGE_HPP #include "../my_config.h" #include #include #include "datetime.hpp" #include "user_interaction.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the etage structure keep trace of directory contents /// it relies on the [fd]opendir() system call family than /// cannot be used recursively. Thus each etage structure /// contains the contents of a directory, and can then be stored beside /// other etage structures corresponding to subdirectories struct etage { etage() { fichier.clear(); last_mod = datetime(0); last_acc = datetime(0); }; // required to fake an empty dir when one is impossible to open etage(user_interaction & ui, const char *dirname, const datetime & x_last_acc, const datetime & x_last_mod, bool cache_directory_tagging, bool furtive_read_mode); etage(const etage & ref) = default; etage(etage && ref) = default; etage & operator = (const etage & ref) = default; etage & operator = (etage && ref) noexcept = default; ~etage() = default; bool read(std::string & ref); std::deque fichier; ///< holds the list of entry in the directory datetime last_mod; ///< the last_lod of the directory itself datetime last_acc; ///< the last_acc of the directory itself }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_specific_attribute.hpp0000644000175000017500000003110014636066467020163 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filesystem_specific_attribute.hpp /// \brief filesystem specific attributes /// \ingroup Private #ifndef FILESYSTEM_SPECIFIC_ATTRIBUTE_HPP #define FILESYSTEM_SPECIFIC_ATTRIBUTE_HPP #include "../my_config.h" extern "C" { #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_UNISTD_H #include #endif } // end extern "C" #include #include #include "integers.hpp" #include "fsa_family.hpp" #include "datetime.hpp" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// Filesystem Specific Attributes (FSA) class /// this class handle the storage of attributes into and from the archive /// it has not filesystem specific knownledge. This aspect is managed /// by filesystem_specific_attribute_list that upon system call will create /// the liste of FSA and given the list of FSA will try to set them back to the /// filesystem class filesystem_specific_attribute { public: /// constructor used to before reading the FSA from filesystem /// \note when the underlying filesystem does not support the requested EA the constructor /// of the inherited class should throw an exception of type Erange. Only valid object should /// be built, that is object containing the value of the FSA found on the filesystem. filesystem_specific_attribute(fsa_family f) { fam = f; nat = fsan_unset; }; /// constructor used to read a FSA from a libdar archive filesystem_specific_attribute(generic_file & f, fsa_family xfam, fsa_nature xnat) { fam = xfam; nat = xnat; }; filesystem_specific_attribute(const filesystem_specific_attribute & ref) = default; filesystem_specific_attribute(filesystem_specific_attribute && ref) noexcept = default; filesystem_specific_attribute & operator = (const filesystem_specific_attribute & ref) = default; filesystem_specific_attribute & operator = (filesystem_specific_attribute && ref) noexcept = default; /// virtual destructor for inherited classes virtual ~filesystem_specific_attribute() noexcept(false) {}; /// provide a mean to compare objects types bool is_same_type_as(const filesystem_specific_attribute & ref) const; /// provides a mean to compare objects values virtual bool operator == (const filesystem_specific_attribute & ref) const { return is_same_type_as(ref) && equal_value_to(ref); }; bool operator != (const filesystem_specific_attribute & ref) const { return ! (*this == ref); }; /// used to provided a sorted list of FSA bool operator < (const filesystem_specific_attribute & ref) const; bool operator >= (const filesystem_specific_attribute & ref) const { return !(*this < ref); }; bool operator > (const filesystem_specific_attribute & ref) const { return ref < *this; }; bool operator <= (const filesystem_specific_attribute & ref) const { return !(*this > ref); }; /// obtain the family of the FSA fsa_family get_family() const { return fam; }; /// obtain the nature of the FSA fsa_nature get_nature() const { return nat; }; /// provides a human readable value of the FSA virtual std::string show_val() const = 0; /// write down to libdar archive virtual void write(generic_file & f) const = 0; /// give the storage size for the FSA virtual infinint storage_size() const = 0; /// provides a way to copy objects without having to know the more specific class of the object virtual filesystem_specific_attribute *clone() const = 0; protected: void set_family(const fsa_family & val) { fam = val; }; void set_nature(const fsa_nature & val) { nat = val; }; /// should return true if the value of the argument is equal to the one of 'this' false in any other case (even for object of another inherited class) virtual bool equal_value_to(const filesystem_specific_attribute & ref) const = 0; private: fsa_family fam; fsa_nature nat; }; /////////////////////////////////////////////////////////////////////////// /// interface to fileystem for FSA class filesystem_specific_attribute_list { public: filesystem_specific_attribute_list() {}; filesystem_specific_attribute_list(const filesystem_specific_attribute_list & ref) { copy_from(ref); }; filesystem_specific_attribute_list & operator = (const filesystem_specific_attribute_list & ref) { clear(); copy_from(ref); return *this; }; ~filesystem_specific_attribute_list() { clear(); }; /// clear all attributes void clear(); /// add an fsa to the list void add(const filesystem_specific_attribute & fsa); /// gives the set of FSA family present in the list fsa_scope get_fsa_families() const { return familes; }; /// compare two lists of FSA to see whether they have equal FSA with identical values within the given family scope bool is_included_in(const filesystem_specific_attribute_list & ref, const fsa_scope & scope) const; /// read FSA list from archive void read(generic_file & f, archive_version ver); /// write FSA list to archive void write(generic_file & f) const; /// read FSA list from filesystem void get_fsa_from_filesystem_for(user_interaction & ui, const std::string & target, const fsa_scope & scope, mode_t itype, bool auto_zeroing_neg_dates); /// return true if this FSA_list contains linux FSA with the immutable attribute set to true bool has_linux_immutable_set() const; /// set FSA list to filesystem /// \param [in] target path of file to restore FSA to /// \param [in] scope list of FSA families to only consider /// \param [in] ui user interaction object /// \param [in] set_linux_immutable whether to restore the only linux immutable flag or ignore it and restore the rest /// \return true if some FSA have effectively been set, false if no FSA /// could be set either because list was empty of all FSA in the list where out of scope bool set_fsa_to_filesystem_for(const std::string & target, const fsa_scope & scope, user_interaction & ui, bool set_linux_immutable) const; /// whether the list has at least one FSA bool empty() const { return fsa.empty(); }; /// access to members of the list U_I size() const { return fsa.size(); }; /// provide reference to FSA given its index const filesystem_specific_attribute & operator [] (U_I arg) const; /// give the storage size for the EA infinint storage_size() const; /// addition operator /// \note this operator is not reflexive (or symetrical if you prefer). Here "a + b" may differ from "b + a" /// all FSA of the arg are added with overwriting to the FSA of 'this' filesystem_specific_attribute_list operator + (const filesystem_specific_attribute_list & arg) const; /// look for the FSA of given familly and nature /// \param[in] fam family of the FSA to look for /// \param[in] nat nature of the FSA to look for /// \param[in, out] ptr points to the FSA if found /// \return true if such an FSA has been found and set ptr accordingly else return false bool find(fsa_family fam, fsa_nature nat, const filesystem_specific_attribute *&ptr) const; private: std::deque fsa; //< sorted list of FSA fsa_scope familes; void copy_from(const filesystem_specific_attribute_list & ref); void update_familes(); void priv_add(const filesystem_specific_attribute & ref); // add an entry without updating the "familes" field void sort_fsa(); void fill_extX_FSA_with(const std::string & target, mode_t itype); void fill_HFS_FSA_with(user_interaction & ui, const std::string & target, mode_t itype, bool auto_zeroing_neg_dates); /// \note return true if some FSA could be set /// \param[in] ui user interaction /// \param[in] target the target to assign the FSA to /// \param[in] set_immutable whether to ignore or set the immutable flag when present bool set_extX_FSA_to(user_interaction & ui, const std::string & target, bool set_immutable) const; /// \note return true if some FSA could be set bool set_hfs_FSA_to(user_interaction & ui, const std::string & target) const; static std::string family_to_signature(fsa_family f); static std::string nature_to_signature(fsa_nature n); static fsa_family signature_to_family(const std::string & sig); static fsa_nature signature_to_nature(const std::string & sig); }; /////////////////////////////////////////////////////////////////////////// template T *cloner(const T *x) { if(x == nullptr) throw SRC_BUG; T *ret = new (std::nothrow) T(*x); if(ret == nullptr) throw Ememory("cloner template"); return ret; } /////////////////////////////////////////////////////////////////////////// class fsa_bool : public filesystem_specific_attribute { public: fsa_bool(fsa_family f, fsa_nature n, bool xval) : filesystem_specific_attribute(f), val(xval) { set_nature(n); }; fsa_bool(generic_file & f, fsa_family fam, fsa_nature nat); fsa_bool(const fsa_bool & ref) = default; fsa_bool & operator = (const fsa_bool & ref) = default; ~fsa_bool() = default; bool get_value() const { return val; }; /// inherited from filesystem_specific_attribute virtual std::string show_val() const { return val ? gettext("true") : gettext("false"); }; virtual void write(generic_file & f) const { f.write(val ? "T" : "F", 1); }; virtual infinint storage_size() const { return 1; }; virtual filesystem_specific_attribute *clone() const { return cloner(this); }; protected: virtual bool equal_value_to(const filesystem_specific_attribute & ref) const; private: bool val; }; /////////////////////////////////////////////////////////////////////////// /// fsa based on integer class fsa_infinint : public filesystem_specific_attribute { public: fsa_infinint(fsa_family f, fsa_nature n, infinint xval) : filesystem_specific_attribute(f), val(xval) { set_nature(n); }; fsa_infinint(generic_file & f, fsa_family fam, fsa_nature nat); fsa_infinint(const fsa_infinint & ref) = default; fsa_infinint & operator = (const fsa_infinint & ref) = default; ~fsa_infinint() = default; const infinint & get_value() const { return val; }; /// inherited from filesystem_specific_attribute virtual std::string show_val() const; virtual void write(generic_file & f) const { val.dump(f); }; virtual infinint storage_size() const { return val.get_storage_size(); }; virtual filesystem_specific_attribute *clone() const { return cloner(this); }; protected: virtual bool equal_value_to(const filesystem_specific_attribute & ref) const; private: infinint val; }; /////////////////////////////////////////////////////////////////////////// /// fsa based on time class fsa_time : public filesystem_specific_attribute { public: fsa_time(fsa_family f, fsa_nature n, datetime xval) : filesystem_specific_attribute(f), val(xval) { set_nature(n); }; fsa_time(generic_file & f, archive_version ver, fsa_family fam, fsa_nature nat); fsa_time(const fsa_time & ref) = default; fsa_time & operator = (const fsa_time & ref) = default; ~fsa_time() = default; const datetime & get_value() const { return val; }; /// inherited from filesystem_specific_attribute virtual std::string show_val() const; virtual void write(generic_file & f) const { val.dump(f); }; virtual infinint storage_size() const { return val.get_storage_size(); }; virtual filesystem_specific_attribute *clone() const { return cloner(this); }; protected: virtual bool equal_value_to(const filesystem_specific_attribute & ref) const; private: datetime val; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/user_interaction_callback.cpp0000644000175000017500000001111614636066467017240 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_FCNTL_H #include #endif #if STDC_HEADERS #include #endif } // end extern "C" #include #include "user_interaction_callback.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "integers.hpp" #include "deci.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { using libdar::Elibcall; using libdar::Ebug; using libdar::Egeneric; using libdar::dar_gettext; using libdar::Ememory; user_interaction_callback::user_interaction_callback(message_callback x_message_callback, pause_callback x_answer_callback, get_string_callback x_string_callback, get_secu_string_callback x_secu_string_callback, void *context_value) { NLS_SWAP_IN; try { if(x_message_callback == nullptr || x_answer_callback == nullptr || x_string_callback == nullptr || x_secu_string_callback == nullptr) throw Elibcall("user_interaction_callback::user_interaction_callback", dar_gettext("nullptr given as argument of user_interaction_callback()")); message_cb = x_message_callback; pause_cb = x_answer_callback; get_string_cb = x_string_callback; get_secu_string_cb = x_secu_string_callback; context_val = context_value; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void user_interaction_callback::inherited_message(const string & message) { if(message_cb == nullptr) throw SRC_BUG; else { try { (*message_cb)(message, context_val); } catch(...) { throw Elibcall("user_interaction_callback::inherited_message", "user provided callback function (user_interaction_callback/message) should not throw any exception"); } } } bool user_interaction_callback::inherited_pause(const string & message) { if(pause_cb == nullptr) throw SRC_BUG; else { try { return (*pause_cb)(message, context_val); } catch(...) { throw Elibcall("user_interaction_callback::inherited_pause", "user provided callback function (user_interaction_callback/pause) should not throw any exception"); } } } string user_interaction_callback::inherited_get_string(const string & message, bool echo) { if(get_string_cb == nullptr) throw SRC_BUG; else { try { return (*get_string_cb)(message, echo, context_val); } catch(...) { throw Elibcall("user_interaction_callback::inherited_get_string", "user provided callback function (user_interaction_callback/get_string) should not throw any exception"); } } } secu_string user_interaction_callback::inherited_get_secu_string(const string & message, bool echo) { if(get_secu_string_cb == nullptr) throw SRC_BUG; else { try { return (*get_secu_string_cb)(message, echo, context_val); } catch(...) { throw Elibcall("user_interaction_callback::inherited_get_secu_string", "user provided callback function (user_interaction_callback/get_secu_string) should not throw any exception"); } } } } // end of namespace dar-2.7.15/src/libdar/libdar_slave.cpp0000644000175000017500000001374014636066467014503 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "nls_swap.hpp" extern "C" { } // end extern "C" #include #include #include "libdar_slave.hpp" #include "path.hpp" #include "macro_tools.hpp" #include "sar.hpp" #include "slave_zapette.hpp" #include "entrepot_local.hpp" #include "tools.hpp" using namespace std; namespace libdar { class libdar_slave::i_libdar_slave { public: i_libdar_slave(shared_ptr & dialog, const string & folder, const string & basename, const string & extension, bool input_pipe_is_fd, const string & input_pipe, bool output_pipe_is_fd, const string & output_pipe, const string & execute, const infinint & min_digits); i_libdar_slave(const i_libdar_slave & ref) = delete; i_libdar_slave(i_libdar_slave && ref) noexcept = delete; i_libdar_slave & operator = (const i_libdar_slave & ref) = delete; i_libdar_slave & operator = (i_libdar_slave && ref) noexcept = delete; ~i_libdar_slave() { zap.reset(); entrep.reset(); }; // the order is important inline void run() { zap->action(); }; private: shared_ptr entrep; ///< must be deleted last unique_ptr zap; ///< must be deleted first (so we need a pointer to it) }; libdar_slave::i_libdar_slave::i_libdar_slave(shared_ptr & dialog, const string & folder, const string & basename, const string & extension, bool input_pipe_is_fd, const string & input_pipe, bool output_pipe_is_fd, const string & output_pipe, const string & execute, const infinint & min_digits) { path chemin(folder); tuyau *input = nullptr; tuyau *output = nullptr; sar *source = nullptr; string base = basename; U_I input_fd; U_I output_fd; if(input_pipe.size() == 0) throw Elibcall("libdar_slave::libdar_slave", "empty string given to argument input_pipe of libdar_slave constructor"); if(output_pipe.size() == 0) throw Elibcall("libdar_slave::libdar_slave", "empty string given to argument input_pipe of libdar_slave constructor"); if(input_pipe_is_fd) if(!tools_my_atoi(input_pipe.c_str(), input_fd)) throw Elibcall("libdar_slave::libdar_slave", "non integer provided to argument input_pipe of libdar_slave constructor while input_pipe_is_fd was set"); if(output_pipe_is_fd) if(!tools_my_atoi(output_pipe.c_str(), output_fd)) throw Elibcall("libdar_slave::libdar_slave", "non integer provided to argument output_pipe of libdar_slave constructor while output_pipe_is_fd was set"); entrep.reset(new (nothrow) entrepot_local("", "", false)); if(!entrep) throw Ememory("libdar_slave::libdar_slave"); entrep->set_location(chemin); try { source = new (nothrow) sar(dialog, base, extension, entrep, true, min_digits, false, false, execute); if(source == nullptr) throw Ememory("libdar_slave::libdar_slave"); if(input_pipe_is_fd) input = new (nothrow) tuyau(dialog, input_fd, gf_read_only); else input = new (nothrow) tuyau(dialog, input_pipe, gf_read_only); if(input == nullptr) throw Ememory("libdar_slave::libdar_slave"); if(output_pipe_is_fd) output = new (nothrow) tuyau(dialog, output_fd, gf_write_only); else output = new (nothrow) tuyau(dialog, output_pipe, gf_write_only); if(output == nullptr) throw Ememory("libdar_slave::libdar_slave"); zap.reset(new (nothrow) slave_zapette(input, output, source)); if(!zap) throw Ememory("libdar_slave::libdar_slave"); input = output = nullptr; // now managed by zap; source = nullptr; // now managed by zap; } catch(...) { if(input != nullptr) delete input; if(output != nullptr) delete output; if(source != nullptr) delete source; throw; } } ////////////// //// libdar_slave public methods libdar_slave::libdar_slave(std::shared_ptr & dialog, const std::string & folder, const std::string & basename, const std::string & extension, bool input_pipe_is_fd, const std::string & input_pipe, bool output_pipe_is_fd, const std::string & output_pipe, const std::string & execute, const infinint & min_digits) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_libdar_slave(dialog, folder, basename, extension, input_pipe_is_fd, input_pipe, output_pipe_is_fd, output_pipe, execute, min_digits)); if(!pimpl) throw Ememory("libdar_slave::libdar_slave"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } libdar_slave::~libdar_slave() = default; void libdar_slave::run() { NLS_SWAP_IN; try { pimpl->run(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } } // end of namespace dar-2.7.15/src/libdar/trontextual.cpp0000644000175000017500000000331614636066467014443 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "trontextual.hpp" namespace libdar { trontextual::trontextual(generic_file *f, const infinint & offset, const infinint & size, bool own_f) : tronc(f, offset, size, own_f) { init(f); } trontextual::trontextual(generic_file *f, const infinint & offset, const infinint & size, gf_mode mode, bool own_f) : tronc(f, offset, size, mode, own_f) { init(f); } void trontextual::init(generic_file *f) { ref = dynamic_cast(f); if(ref == nullptr) throw Erange("trontextual::trontextual", "Trontextual must receive a class contextual aware generic file as argument"); } } // end of namespace dar-2.7.15/src/libdar/entrepot_libcurl.cpp0000644000175000017500000001515414636067146015424 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "tools.hpp" #include "fichier_libcurl.hpp" #include "cache_global.hpp" #include "nls_swap.hpp" #include "entrepot_libcurl.hpp" #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) #include "i_entrepot_libcurl.hpp" #endif using namespace std; namespace libdar { entrepot_libcurl::entrepot_libcurl(const shared_ptr & dialog, //< for user interaction mycurl_protocol proto, ///< network protocol to use const string & login, ///< user login on remote host const secu_string & password, ///< user password on remote host (empty for file auth or user interaction) const string & host, ///< the remote server to connect to const string & port, ///< TCP/UDP port to connec to (empty string for default) bool auth_from_file, ///< whether to check $HOME/.netrc for password const string & sftp_pub_keyfile, ///< where to fetch the public key (sftp only) const string & sftp_prv_keyfile, ///< where to fetch the private key (sftp only) const string & sftp_known_hosts, ///< location of the known_hosts file (empty string to disable this security check) U_I waiting_time, bool verbose ///< whether to have verbose messages from libcurl ) { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_entrepot_libcurl(dialog, proto, login, password, host, port, auth_from_file, sftp_pub_keyfile, sftp_prv_keyfile, sftp_known_hosts, waiting_time, verbose)); if(!pimpl) throw Ememory("entrepot_libcurl::entrepot_libcurl"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; #else throw Ecompilation("libcurl library"); #endif } void entrepot_libcurl::set_location(const path & chemin) { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) pimpl->set_location(chemin); #endif } void entrepot_libcurl::set_root(const path & p_root) { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) pimpl->set_root(p_root); #endif } path entrepot_libcurl::get_full_path() const { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) return pimpl->get_full_path(); #else return path("/"); #endif } string entrepot_libcurl::get_url() const { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) string ret; NLS_SWAP_IN; try { ret = pimpl->get_url(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; #else throw Efeature("libcurl library"); #endif } const path & entrepot_libcurl::get_location() const { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) return pimpl->get_location(); #else throw Ecompilation("Elibcurl or libthreadar"); #endif } const path & entrepot_libcurl::get_root() const { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) return pimpl->get_root(); #else throw Ecompilation("Elibcurl or libthreadar"); #endif } void entrepot_libcurl::read_dir_reset() const { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) NLS_SWAP_IN; try { pimpl->read_dir_reset(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; #else throw Efeature("libcurl library"); #endif } bool entrepot_libcurl::read_dir_next(std::string & filename) const { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) bool ret; NLS_SWAP_IN; try { ret = pimpl->read_dir_next(filename); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; #else throw Efeature("libcurl library"); #endif } fichier_global *entrepot_libcurl::inherited_open(const shared_ptr & dialog, const string & filename, gf_mode mode, bool force_permission, U_I permission, bool fail_if_exists, bool erase) const { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) fichier_global* ret; NLS_SWAP_IN; try { ret = pimpl->inherited_open(dialog, filename, mode, force_permission, permission, fail_if_exists, erase); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; #else throw Efeature("libcurl library"); #endif } void entrepot_libcurl::inherited_unlink(const string & filename) const { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) NLS_SWAP_IN; try { pimpl->inherited_unlink(filename); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; #else throw Efeature("libcurl library"); #endif } void entrepot_libcurl::read_dir_flush() { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) NLS_SWAP_IN; try { pimpl->read_dir_flush(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; #else throw Efeature("libcurl library"); #endif } } // end of namespace dar-2.7.15/src/libdar/fichier_global.hpp0000644000175000017500000001274714636066467015020 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file fichier_global.hpp /// \brief class fichier_global definition. This class is a pure virtual class /// class fichier_global is an abstraction of files objects whatever is their localisation /// like local filesystem, remote ftp server, etc. inherited classes (like fichier_local) /// provide full implementation /// \ingroup Private #ifndef FICHIER_GLOBAL_HPP #define FICHIER_GLOBAL_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "integers.hpp" #include "generic_file.hpp" #include "thread_cancellation.hpp" #include "user_interaction.hpp" #include "mem_ui.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// abstraction of filesystem files for entrepot class fichier_global : public generic_file, public thread_cancellation, public mem_ui { public : enum advise { advise_normal, ///< no advise given by the application advise_sequential, ///< application expect to read the data sequentially advise_random, ///< application expect to read the data in random order advise_noreuse, ///< application does not expect to read the data more than once advise_willneed, ///< application expect to read the data again in near future advise_dontneed ///< application will not read the data in near future }; /// constructor /// \note some well defined error case must generate an Esystem exception, other by Erange or /// more appropriated Egeneric exceptions to known what type of error must be handled /// by Esystem object, see the Esystem::io_error enum fichier_global(const std::shared_ptr & dialog, gf_mode mode): generic_file(mode), mem_ui(dialog), disk_full(false) {}; /// copy constructor fichier_global(const fichier_global & ref) = default; /// move constructor fichier_global(fichier_global && ref) noexcept = default; /// assignment operator fichier_global & operator = (const fichier_global & ref) = default; /// move operator fichier_global & operator = (fichier_global && ref) noexcept = default; // destructor ~fichier_global() = default; /// set the ownership of the file virtual void change_ownership(const std::string & user, const std::string & group) = 0; /// change the permission of the file virtual void change_permission(U_I perm) = 0; /// return the size of the file virtual infinint get_size() const = 0; /// set posix_fadvise for the whole file virtual void fadvise(advise adv) const = 0; protected : /// replaces generic_file::inherited_write() method, to allow the return of partial writings /// a partial writing is allowed when no space is available for writing /// this let global_ficher interact with the user asking whether it can make place /// or if (s)he wants to abort /// \param[in] a points to the start of the area of data to write /// \param[in] size is the size in byte of the data to write /// \return the amount of byte wrote. If the returned value is less than size, this /// is a partial write, and is assumed that free storage space is missing to complete /// the operation virtual U_I fichier_global_inherited_write(const char *a, U_I size) = 0; /// replaces generic_file::inherited_read() method, to allow the return of partial reading /// a partial reading is signaled by the inherited class by returning false /// \param[in] a points to the area where to store read data /// \param[in] size is the available place to store data /// \param[out] read is the total amount of data read so far /// \param[out] message is the request to send to the user upon partial reading /// \return true if the reading is full (either read the maximum allowed data or reached end of file) /// false is returned if a user interaction can let the reading go further the message to display to the /// user asking him for action. He will also be proposed to abort the current operation. virtual bool fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) = 0; private: bool disk_full; ///< whether user refused to continue due to disk being full // inherited from generic_file class and relocated as private methods virtual void inherited_write(const char *a, U_I size) override; virtual U_I inherited_read(char *a, U_I size) override; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/libdar5.cpp0000644000175000017500000002526614636067146013377 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_SYS_TYPES_H #include #endif #if HAVE_UNISTD_H #include #endif #if LIBLZO2_AVAILABLE #if HAVE_LZO_LZO1X_H #include #endif #endif #if HAVE_STDLIB_H #include #endif #if HAVE_GCRYPT_H #ifndef GCRYPT_NO_DEPRECATED #define GCRYPT_NO_DEPRECATED #endif #include #endif #if HAVE_TIME_H #include #endif #if HAVE_GPGME_H #include #endif } // end extern "C" #include #include #include "libdar5.hpp" #include "erreurs.hpp" #include "infinint.hpp" #include "generic_file.hpp" #include "user_interaction5.hpp" #include "archive5.hpp" #include "nls_swap.hpp" #ifdef __DYNAMIC__ #include "user_group_bases.hpp" #endif #include "get_version.hpp" #include using namespace std; #define WRAPPER_IN try { // #define WRAPPER_OUT(code,msg) \ code = LIBDAR_NOEXCEPT; \ } \ catch(Ememory & e) \ { \ code = LIBDAR_EMEMORY; \ msg = e.get_message(); \ } \ catch(Ebug & e) \ { \ code = LIBDAR_EBUG; \ msg = e.get_message(); \ } \ catch(Einfinint & e) \ { \ code = LIBDAR_EINFININT; \ msg = e.get_message(); \ } \ catch(Elimitint & e) \ { \ code = LIBDAR_ELIMITINT; \ msg = e.get_message(); \ } \ catch(Erange & e) \ { \ code = LIBDAR_ERANGE; \ msg = e.get_message(); \ } \ catch(Edeci & e) \ { \ code = LIBDAR_EDECI; \ msg = e.get_message(); \ } \ catch(Efeature & e) \ { \ code = LIBDAR_EFEATURE; \ msg = e.get_message(); \ } \ catch(Ehardware & e) \ { \ code = LIBDAR_EHARDWARE; \ msg = e.get_message(); \ } \ catch(Euser_abort & e) \ { \ code = LIBDAR_EUSER_ABORT; \ msg = e.get_message(); \ } \ catch(Edata & e) \ { \ code = LIBDAR_EDATA; \ msg = e.get_message(); \ } \ catch(Escript & e) \ { \ code = LIBDAR_ESCRIPT; \ msg = e.get_message(); \ } \ catch(Elibcall & e) \ { \ code = LIBDAR_ELIBCALL; \ msg = e.get_message(); \ } \ catch(Ecompilation & e) \ { \ code = LIBDAR_ECOMPILATION; \ msg = e.get_message(); \ } \ catch(Ethread_cancel & e) \ { \ code = LIBDAR_THREAD_CANCEL; \ msg = e.get_message(); \ } \ catch(Egeneric & e) \ {/*unknown Egeneric exception*/ \ code = LIBDAR_EBUG; \ msg = string(gettext("Caught an unknown Egeneric exception: ")) + e.get_message(); \ } \ catch(...) \ {/* unknown Egeneric exception*/ \ code = LIBDAR_UNKNOWN; \ msg = gettext("Caught a none libdar exception"); \ } // namespace libdar5 { void get_version(U_I & major, U_I & medium, U_I & minor, bool init_libgcrypt) { libdar::get_version(major, medium, minor, init_libgcrypt); major = LIBDAR_COMPILE_TIME_MAJOR; medium = LIBDAR_COMPILE_TIME_MEDIUM; minor = LIBDAR_COMPILE_TIME_MINOR; } void get_version_noexcept(U_I & major, U_I & medium, U_I & minor, U_16 & exception, std::string & except_msg, bool init_libgcrypt) { NLS_SWAP_IN; WRAPPER_IN get_version(major, medium, minor, init_libgcrypt); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; } void close_and_clean() { libdar::close_and_clean(); } archive* open_archive_noexcept(user_interaction & dialog, const path & chem, const std::string & basename, const std::string & extension, const archive_options_read & options, U_16 & exception, std::string & except_msg) { archive *ret = nullptr; NLS_SWAP_IN; WRAPPER_IN ret = new (nothrow) archive(dialog, chem, basename, extension, options); if(ret == nullptr) throw Ememory("open_archive_noexcept"); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; return ret; } archive *create_archive_noexcept(user_interaction & dialog, const path & fs_root, const path & sauv_path, const std::string & filename, const std::string & extension, const archive_options_create & options, statistics * progressive_report, U_16 & exception, std::string & except_msg) { archive *arch_ret = nullptr; NLS_SWAP_IN; WRAPPER_IN arch_ret = new (nothrow) archive(dialog, fs_root, sauv_path, filename, extension, options, progressive_report); if(arch_ret == nullptr) throw Ememory("open_archive_noexcept"); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; return arch_ret; } void op_isolate_noexcept(user_interaction & dialog, archive *ptr, const path &sauv_path, const std::string & filename, const std::string & extension, const archive_options_isolate & options, U_16 & exception, std::string & except_msg) { NLS_SWAP_IN; WRAPPER_IN if(ptr == nullptr) throw Elibcall("op_isolate_noexcept", gettext("Invald nullptr argument given to 'ptr'")); ptr->op_isolate(dialog, sauv_path, filename, extension, options); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; } archive *merge_archive_noexcept(user_interaction & dialog, const path & sauv_path, archive *ref_arch1, const std::string & filename, const std::string & extension, const archive_options_merge & options, statistics * progressive_report, U_16 & exception, std::string & except_msg) { archive *ret = nullptr; NLS_SWAP_IN; WRAPPER_IN ret = new (nothrow) archive(dialog, sauv_path, ref_arch1, filename, extension, options, progressive_report); if(ret == nullptr) throw Ememory("open_archive_noexcept"); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; return ret; } void close_archive_noexcept(archive *ptr, U_16 & exception, std::string & except_msg) { NLS_SWAP_IN; WRAPPER_IN if(ptr == nullptr) throw Elibcall("close_archive_noexcept", gettext("Invalid nullptr pointer given to close_archive")); else { delete ptr; ptr = nullptr; } WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; } statistics op_extract_noexcept(user_interaction & dialog, archive *ptr, const path &fs_root, const archive_options_extract & options, statistics * progressive_report, U_16 & exception, std::string & except_msg) { statistics ret; NLS_SWAP_IN; WRAPPER_IN if(ptr == nullptr) throw Elibcall("op_extract_noexcept", gettext("Invalid nullptr argument given to 'ptr'")); ret = ptr->op_extract(dialog, fs_root, options, progressive_report); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; return ret; } void op_listing_noexcept(user_interaction & dialog, archive *ptr, const archive_options_listing & options, U_16 & exception, std::string & except_msg) { NLS_SWAP_IN; WRAPPER_IN if(ptr == nullptr) throw Elibcall("op_extract_noexcept", gettext("Invalid nullptr argument given to 'ptr'")); ptr->op_listing(dialog, options); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; } statistics op_diff_noexcept(user_interaction & dialog, archive *ptr, const path & fs_root, const archive_options_diff & options, statistics * progressive_report, U_16 & exception, std::string & except_msg) { statistics ret; NLS_SWAP_IN; WRAPPER_IN if(ptr == nullptr) throw Elibcall("op_extract_noexcept", gettext("Invalid nullptr argument given to 'ptr'")); ret = ptr->op_diff(dialog, fs_root, options, progressive_report); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; return ret; } statistics op_test_noexcept(user_interaction & dialog, archive *ptr, const archive_options_test & options, statistics * progressive_report, U_16 & exception, std::string & except_msg) { statistics ret; NLS_SWAP_IN; WRAPPER_IN if(ptr == nullptr) throw Elibcall("op_extract_noexcept", gettext("Invalid nullptr argument given to 'ptr'")); ret = ptr->op_test(dialog, options, progressive_report); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; return ret; } bool get_children_of_noexcept(user_interaction & dialog, archive *ptr, const std::string & dir, U_16 & exception, std::string & except_msg) { bool ret = false; NLS_SWAP_IN; WRAPPER_IN if(ptr == nullptr) throw Elibcall("op_extract_noexcept", gettext("Invalid nullptr argument given to 'ptr'")); ret = ptr->get_children_of(dialog, dir); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; return ret; } char *libdar_str2charptr_noexcept(const std::string & x, U_16 & exception, std::string & except_msg) { char *ret = nullptr; NLS_SWAP_IN; WRAPPER_IN ret = libdar::tools_str2charptr(x); WRAPPER_OUT(exception, except_msg) NLS_SWAP_OUT; return ret; } #if MUTEX_WORKS void cancel_thread(pthread_t tid, bool immediate, U_64 flag) { libdar::cancel_thread(tid, immediate, flag); } bool cancel_status(pthread_t tid) { return libdar::cancel_status(tid); } bool cancel_clear(pthread_t tid) { return libdar::cancel_clear(tid); } #endif } // end of namespace dar-2.7.15/src/libdar/tronc.cpp0000644000175000017500000001677014636066467013207 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif } // end extern "C" #include "tronc.hpp" namespace libdar { tronc::tronc(generic_file *f, const infinint & offset, const infinint &size, bool own_f) : generic_file(f->get_mode()) { ref = f; sz = size; start = offset; current = size; // forces skipping the first time own_ref = own_f; limited = true; check_pos = true; } tronc::tronc(generic_file *f, const infinint & offset, const infinint &size, gf_mode mode, bool own_f) : generic_file(mode) { ref = f; sz = size; start = offset; current = size; // forces skipping the firt time own_ref = own_f; limited = true; check_pos = true; } tronc::tronc(generic_file *f, const infinint &offset, bool own_f) : generic_file(f->get_mode()) { ref = f; sz = 0; start = offset; current = 0; own_ref = own_f; limited = false; check_pos = true; } tronc::tronc(generic_file *f, const infinint &offset, gf_mode mode, bool own_f) : generic_file(mode) { ref = f; sz = 0; start = offset; current = 0; own_ref = own_f; limited = false; check_pos = true; } void tronc::modify(const infinint & new_offset, const infinint & new_size) { modify(new_offset); sz = new_size; limited = true; if(current > sz) current = sz; } void tronc::modify(const infinint & new_offset) { current = current + start; // current now temporarily holds the absolute position start = new_offset; if(current <= start) current = 0; else current -= start; // now current points to the same byte but within the new scope limited = false; } bool tronc::skippable(skippability direction, const infinint & amount) { if(is_terminated()) throw SRC_BUG; return ref->skippable(direction, amount); } bool tronc::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; if(current == pos && check_pos) // we skip anyway when check_pos is false return true; if(limited && pos > sz) { if(ref->skip(start + sz)) current = sz; else (void)ref->skip(start + current); return false; } else { bool ret = ref->skip(start + pos); if(ret) current = pos; else (void)ref->skip(start + current); return ret; } } bool tronc::skip_to_eof() { bool ret; if(is_terminated()) throw SRC_BUG; if(limited) { ret = ref->skip(start + sz); if(ret) current = sz; else (void)ref->skip(start + current); } else { ret = ref->skip_to_eof(); if(ret) set_back_current_position(); else (void)skip(start + current); } return ret; } bool tronc::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; if(x < 0) { if(current < -x) { (void)ref->skip(start); current = 0; return false; } else { bool r = ref->skip_relative(x); if(r) current -= -x; else ref->skip(start + current); return r; } } if(x > 0) { if(limited && current + x >= sz) { current = sz; (void)ref->skip(start + sz); return false; } else { bool r = ref->skip_relative(x); if(r) current += x; else (void)ref->skip(start + current); return r; } } return true; } void tronc::inherited_read_ahead(const infinint & amount) { if(limited) { infinint avail = sz - current; if(avail > amount) ref->read_ahead(amount); else ref->read_ahead(avail); } else ref->read_ahead(amount); } U_I tronc::inherited_read(char *a, U_I size) { U_I lu = 0; infinint abso_pos = start + current; if(check_pos && ref->get_position() != abso_pos) { if(!ref->skip(abso_pos)) throw Erange("tronc::inherited_read", gettext("Cannot skip to the current position in \"tronc\"")); } if(limited) { infinint avail = sz - current; U_32 macro_pas = 0, micro_pas; U_I ret; do { avail.unstack(macro_pas); micro_pas = size - lu > macro_pas ? macro_pas : size - lu; if(micro_pas > 0) { ret = ref->read(a+lu, micro_pas); if(ret > 0) { lu += ret; macro_pas -= ret; } // else ret = 0 and this ends the while loop } else ret = 0; } while(ret > 0); } else lu = ref->read(a, size); current += lu; return lu; } void tronc::inherited_write(const char *a, U_I size) { U_I wrote = 0; if(check_pos) { if(!ref->skip(start + current)) throw Erange("tronc::inherited_read", gettext("Cannot skip to the current position in \"tronc\"")); } if(limited) { infinint avail = sz - current; U_32 macro_pas = 0, micro_pas; do { avail.unstack(macro_pas); if(macro_pas == 0 && wrote < size) throw Erange("tronc::inherited_write", gettext("Tried to write out of size limited file")); micro_pas = size - wrote > macro_pas ? macro_pas : size - wrote; ref->write(a+wrote, micro_pas); wrote += micro_pas; macro_pas -= micro_pas; } while(wrote < size); } else { ref->write(a, size); wrote = size; } current += wrote; } void tronc::inherited_truncate(const infinint & pos) { if(limited) { if(pos < sz) ref->truncate(start + pos); // else there is nothing to do } else ref->truncate(start + pos); set_back_current_position(); } void tronc::set_back_current_position() { if(is_terminated()) throw SRC_BUG; infinint ref_pos = ref->get_position(); if(ref_pos < start) throw SRC_BUG; if(limited) { if(ref_pos > start + sz) throw SRC_BUG; else current = ref_pos - start; } else current = ref_pos - start; } } // end of namespace dar-2.7.15/src/libdar/cat_etoile.cpp0000644000175000017500000000401014636066467014152 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include #include "cat_etoile.hpp" #include "cat_directory.hpp" using namespace std; namespace libdar { cat_etoile::cat_etoile(cat_inode *host, const infinint & etiquette_number) { if(host == nullptr) throw SRC_BUG; if(dynamic_cast(host) != nullptr) throw Erange("cat_etoile::cat_etoile", gettext("Hard links of directories are not supported")); hosted = host; etiquette = etiquette_number; refs.clear(); } void cat_etoile::add_ref(void *ref) { if(find(refs.begin(), refs.end(), ref) != refs.end()) throw SRC_BUG; // this reference is already known refs.push_back(ref); } void cat_etoile::drop_ref(void *ref) { list::iterator it = find(refs.begin(), refs.end(), ref); if(it == refs.end()) throw SRC_BUG; // cannot drop a reference that does not exist refs.erase(it); if(refs.size() == 0) delete this; } } // end of namespace dar-2.7.15/src/libdar/cat_all_entrees.hpp0000644000175000017500000000330014636066467015174 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_all_entrees.hpp /// \brief include file gathering all entree found in a catalogue /// \ingroup Private #ifndef CAT_ALL_ENTREE_HPP #define CAT_ALL_ENTREE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_detruit.hpp" #include "cat_device.hpp" #include "cat_directory.hpp" #include "cat_door.hpp" #include "cat_entree.hpp" #include "cat_etoile.hpp" #include "cat_file.hpp" #include "cat_ignored_dir.hpp" #include "cat_inode.hpp" #include "cat_lien.hpp" #include "cat_mirage.hpp" #include "cat_nomme.hpp" #include "cat_eod.hpp" #include "cat_chardev.hpp" #include "cat_blockdev.hpp" #include "cat_tube.hpp" #include "cat_prise.hpp" #include "cat_ignored.hpp" #endif dar-2.7.15/src/libdar/cat_mirage.cpp0000644000175000017500000002467514636066467014160 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include #include "cat_mirage.hpp" #include "cat_file.hpp" #include "cat_directory.hpp" #define MIRAGE_ALONE 'X' #define MIRAGE_WITH_INODE '>' using namespace std; namespace libdar { cat_mirage::cat_mirage(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, entree_stats & stats, std::map & corres, compression default_algo, mirage_format fmt, bool lax, bool small) : cat_nomme(pdesc, small, saved_status::saved) { init(dialog, pdesc, reading_ver, saved, stats, corres, default_algo, fmt, lax, small); } cat_mirage::cat_mirage(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, entree_stats & stats, std::map & corres, compression default_algo, bool lax, bool small): cat_nomme("TEMP", saved_status::saved) { init(dialog, pdesc, reading_ver, saved, stats, corres, default_algo, fmt_file_etiquette, lax, small); } void cat_mirage::init(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, entree_stats & stats, std::map & corres, compression default_algo, mirage_format fmt, bool lax, bool small) { infinint tmp_tiquette; char tmp_flag; map::iterator etl; cat_inode *ino_ptr = nullptr; cat_entree *entree_ptr = nullptr; entree_stats fake_stats; // the call to cat_entree::read will increment counters with the inode we will read // but this inode will also be counted from the cat_entree::read we are called from. // thus we must not increment the real entree_stats structure here and we use a local variable generic_file *ptr = nullptr; pdesc->check(small); if(small) ptr = pdesc->esc; else ptr = pdesc->stack; if(fmt != fmt_file_etiquette) tmp_tiquette = infinint(*ptr); switch(fmt) { case fmt_mirage: ptr->read(&tmp_flag, 1); break; case fmt_hard_link: tmp_flag = MIRAGE_ALONE; break; case fmt_file_etiquette: tmp_flag = MIRAGE_WITH_INODE; break; default: throw SRC_BUG; } switch(tmp_flag) { case MIRAGE_ALONE: // we must link with the already existing cat_etoile etl = corres.find(tmp_tiquette); if(etl == corres.end()) throw Erange("cat_mirage::cat_mirage", gettext("Incoherent catalogue structure: hard linked inode's data not found")); else { if(etl->second == nullptr) throw SRC_BUG; star_ref = etl->second; star_ref->add_ref(this); } break; case MIRAGE_WITH_INODE: // we first read the attached inode if(fmt == fmt_file_etiquette) { cat_nomme *tmp_ptr = new (nothrow) cat_file(dialog, pdesc, reading_ver, saved, default_algo, small); entree_ptr = tmp_ptr; if(tmp_ptr != nullptr) { change_name(tmp_ptr->get_name()); tmp_ptr->change_name(""); tmp_tiquette = infinint(*ptr); } else throw Ememory("cat_mirage::init"); } else entree_ptr = cat_entree::read(dialog, pdesc, reading_ver, fake_stats, corres, default_algo, lax, false, small); ino_ptr = dynamic_cast(entree_ptr); if(ino_ptr == nullptr || dynamic_cast(entree_ptr) != nullptr) { if(entree_ptr != nullptr) { delete entree_ptr; entree_ptr = nullptr; } throw Erange("cat_mirage::cat_mirage", gettext("Incoherent catalogue structure: hard linked data is not an inode")); } // then we can bind the inode to the next to be create cat_etoile object try { // we must check that an already exiting cat_etoile is not present etl = corres.find(tmp_tiquette); if(etl == corres.end()) { // we can now create the cat_etoile and add it in the corres map; star_ref = new (nothrow) cat_etoile(ino_ptr, tmp_tiquette); try { if(star_ref == nullptr) throw Ememory("cat_mirage::cat_mirage"); ino_ptr = nullptr; // the object pointed to by ino_ptr is now managed by star_ref star_ref->add_ref(this); corres[tmp_tiquette] = star_ref; } catch(...) { if(star_ref != nullptr) { delete star_ref; star_ref = nullptr; } etl = corres.find(tmp_tiquette); if(etl != corres.end()) corres.erase(etl); throw; } } else throw Erange("cat_mirage::cat_mirage", gettext("Incoherent catalogue structure: duplicated hard linked inode's data")); } catch(...) { if(ino_ptr != nullptr) { delete ino_ptr; ino_ptr = nullptr; } throw; } break; default: throw Erange("cat_mirage::cat_mirage", gettext("Incoherent catalogue structure: unknown status flag for hard linked inode")); } } cat_mirage & cat_mirage::operator = (const cat_mirage & ref) { if(ref.star_ref == nullptr) throw SRC_BUG; // copying the cat_nomme part of these objects cat_nomme::operator = (ref); if(ref.star_ref != star_ref) { ref.star_ref->add_ref(this); star_ref->drop_ref(this); star_ref = ref.star_ref; } return *this; } cat_mirage & cat_mirage::operator = (cat_mirage && ref) { // moving the cat_nomme part of these objects cat_nomme::operator = (move(ref)); if(ref.star_ref != nullptr) { if(ref.star_ref != star_ref) { ref.star_ref->add_ref(this); star_ref->drop_ref(this); star_ref = ref.star_ref; } } // else this is a bug condition, but we are inside an noexcept method return *this; } void cat_mirage::post_constructor(const pile_descriptor & pdesc) { cat_nomme::post_constructor(pdesc); if(star_ref == nullptr) throw SRC_BUG; if(star_ref->get_ref_count() == 1) // first time this inode is seen star_ref->get_inode()->post_constructor(pdesc); } bool cat_mirage::operator == (const cat_entree & ref) const { const cat_mirage *ref_mirage = dynamic_cast(&ref); if(ref_mirage == nullptr) return false; else { cat_inode *me = get_inode(); cat_inode *you = ref_mirage->get_inode(); if(me == nullptr || you == nullptr) throw SRC_BUG; me->change_name(get_name()); you->change_name(get_name()); return *me == *you && cat_nomme::operator == (ref); } } void cat_mirage::inherited_dump(const pile_descriptor & pdesc, bool small) const { generic_file *ptr = nullptr; pdesc.check(small); if(small) ptr = pdesc.esc; else ptr = pdesc.stack; if(star_ref->get_ref_count() > 1 || star_ref->cannot_reduce_to_normal_inode()) { char buffer[] = { MIRAGE_ALONE, MIRAGE_WITH_INODE }; cat_nomme::inherited_dump(pdesc, small); star_ref->get_etiquette().dump(*ptr); if((small && !is_inode_wrote()) // inside the archive in sequential mode || (!small && !is_inode_dumped())) // in the catalogue at the end of archive { ptr->write(buffer+1, 1); // writing one char MIRAGE_WITH_INODE star_ref->get_inode()->specific_dump(pdesc, small); if(!small) set_inode_dumped(true); } else ptr->write(buffer, 1); // writing one char MIRAGE_ALONE } else // no need to record this inode with the hard link overhead { cat_inode *real = star_ref->get_inode(); real->change_name(get_name()); // set the name of the cat_mirage object to the inode real->specific_dump(pdesc, small); } } void cat_mirage::dup_on(cat_etoile * ref) { star_ref = ref; star_ref->add_ref(this); } } // end of namespace dar-2.7.15/src/libdar/cat_entree.hpp0000644000175000017500000002242214636066467014167 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_entree.hpp /// \brief base class for all object contained in a catalogue /// \ingroup Private #ifndef CAT_ENTREE_HPP #define CAT_ENTREE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "infinint.hpp" #include "user_interaction.hpp" #include "pile.hpp" #include "escape.hpp" #include "archive_version.hpp" #include "proto_compressor.hpp" #include "pile_descriptor.hpp" #include "smart_pointer.hpp" #include "entree_stats.hpp" #include "list_entry.hpp" #include "slice_layout.hpp" #include namespace libdar { class cat_etoile; /// \addtogroup Private /// @{ /// the root class from all other inherite for any entry in the catalogue class cat_entree { public : /// read and create an object of inherited class of class cat_entree /// \param[in] dialog for user interaction /// \param[in] f where from to read data in order to create the object /// \param[in] reading_ver archive version format to use for reading /// \param[in,out] stats updated statistical fields /// \param[in,out] corres used to setup hard links /// \param[in] default_algo default compression algorithm /// \param[in] lax whether to use relax mode /// \param[in] only_detruit whether to only consider detruit objects (in addition to the directory tree) /// \param[in] small whether the dump() to read has been done with the small argument set static cat_entree *read(const std::shared_ptr & dialog, const smart_pointer & f, const archive_version & reading_ver, entree_stats & stats, std::map & corres, compression default_algo, bool lax, bool only_detruit, bool small); /// setup an object when read from filesystem cat_entree(saved_status val): xsaved(val) {}; /// setup an object when read from an archive /// \param[in] pdesc points to an existing stack that will be read from to setup fields of inherited classes, /// this pointed to pile object must survive the whole life of the cat_entree object /// \param[in] small whether a small or a whole read is to be read, (inode has been dump() with small set to true) /// \param[in] val saved_status to assign the the new object cat_entree(const smart_pointer & pdesc, bool small, saved_status val); // copy constructor is fine as we only copy the address of pointers cat_entree(const cat_entree & ref) = default; // move constructor cat_entree(cat_entree && ref) noexcept = default; // assignment operator is fine too for the same reason cat_entree & operator = (const cat_entree & ref) = default; // move assignment operator cat_entree & operator = (cat_entree && ref) = default; /// destructor virtual ~cat_entree() noexcept(false) {}; /// returns true if the two object are the same virtual bool operator == (const cat_entree & ref) const = 0; bool operator != (const cat_entree & ref) const { return ! (*this == ref); }; /// return true of the two objects would generate the same entry on filsystem /// for a and b two cat_entree, if a == b, then a.same_as(b) is true also. /// But the opposit may be wrong if for example "a" is a hardlink pointing to an inode /// while b is a normal inode, restoring both could lead to having the same entry in /// filsystem while a and b are different for libdar: here they are objects of two /// different classes. bool same_as(const cat_entree & ref) const { return true; }; /// write down the object information to a stack /// \param[in,out] pdesc is the stack where to write the data to /// \param[in] small defines whether to do a small or normal dump void dump(const pile_descriptor & pdesc, bool small) const; /// this call gives an access to inherited_dump /// \param[in,out] pdesc is the stack where to write the data to /// \param[in] small defines whether to do a small or normal dump void specific_dump(const pile_descriptor & pdesc, bool small) const { inherited_dump(pdesc, small); }; /// let inherited classes build object's data after CRC has been read from file in small read mode /// \param[in] pdesc stack to read the data from /// \note used from cat_entree::read to complete small read /// \note this method is called by cat_entree::read and mirage::post_constructor only when contructing an object with small set to true virtual void post_constructor(const pile_descriptor & pdesc) {}; /// inherited class signature virtual unsigned char signature() const = 0; /// inherited class designation virtual std::string get_description() const = 0; /// a way to copy the exact type of an object even if pointed to by a parent class pointer virtual cat_entree *clone() const = 0; /// for archive merging, will let the object drop EA, FSA and Data /// to an alternate stack than the one it has been read from /// \note this is used when cloning an object from a catalogue to provide a /// merged archive. Such cloned object must point /// the stack of the archive under construction, so we use this call for that need, /// \note this is also used when opening a catalogue if an isolated /// catalogue in place of the internal catalogue of an archive /// \note this method is virtual in order for cat_directory to /// overwrite it and propagate the change to all entries of the directory tree /// as well for mirage to propagate the change to the hard linked inode virtual void change_location(const smart_pointer & pdesc); /// obtain the saved status of the object saved_status get_saved_status() const { return xsaved; }; /// modify the saved_status of the object void set_saved_status(saved_status x) { xsaved = x; }; /// setup a list_entry object relative to the current cat_entree object /// \param[in] sly the slice layout that shall be used for file location in slices /// \param[in] fetch_ea whether to fetch EA and fill these values into the generated list_entry /// \param[out] ent the list_entry that will be setup by the call /// \note if sly is set to nullptr, no slice location is performed, this /// brings some speed improvement when this information is not required void set_list_entry(const slice_layout *sly, bool fetch_ea, list_entry & ent) const; protected: /// inherited class may overload this method but shall first call the parent's inherited_dump() in the overloaded method virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const; /// stack used to read object from (nullptr is returned for object created from filesystem) pile *get_pile() const { return pdesc.is_null() ? nullptr : pdesc->stack; }; /// compressor generic_file relative methods /// \note CAUTION: the pointer to object is member of the get_pile() stack and may be managed by another thread /// all precaution like get_pile()->flush_read_above(get_compressor_layer() shall be take to avoid /// concurrent access to the proto_compressor object by the current thread and the thread managing this object proto_compressor *get_compressor_layer() const { return pdesc.is_null() ? nullptr : pdesc->compr; }; /// escape generic_file relative methods /// \note CAUTION: the pointer to object is member of the get_pile() stack and may be managed by another thread /// all precaution like get_pile()->flush_read_above(get_escape_layer() shall be take to avoid /// concurrent access to the compressor object by the current thread and the thread managing this object escape *get_escape_layer() const { return pdesc.is_null() ? nullptr : pdesc->esc; }; /// return the adhoc layer in the stack to read from the catalogue objects (except the EA, FSA or Data part) generic_file *get_read_cat_layer(bool small) const; private: static const U_I ENTREE_CRC_SIZE; saved_status xsaved; ///< inode data status, this field is stored with signature() in the cat_signature field -> must be managed by cat_entree smart_pointer pdesc; }; /// convert a signature char to a human readable string extern const char *cat_entree_signature2string(unsigned char sign); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/pile_descriptor.cpp0000644000175000017500000000301014636066467015230 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "pile_descriptor.hpp" using namespace std; namespace libdar { pile_descriptor::pile_descriptor(pile *ptr) { if(ptr == nullptr) throw SRC_BUG; stack = ptr; ptr->find_first_from_top(compr); ptr->find_first_from_bottom(esc); } void pile_descriptor::check(bool small) const { if(stack == nullptr) throw SRC_BUG; if(esc == nullptr && small) throw SRC_BUG; if(compr == nullptr) throw SRC_BUG; } } // end of namespace dar-2.7.15/src/libdar/archive_summary.cpp0000644000175000017500000000273214636066467015251 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // extern "C" #include "archive_summary.hpp" using namespace std; namespace libdar { void archive_summary::clear() { slice_size = 0; first_slice_size = 0; last_slice_size = 0; slice_number = 0; archive_size = 0; catalog_size = 0; storage_size = 0; data_size = 0; contents.clear(); edition = ""; algo_zip = ""; user_comment = ""; cipher = ""; asym = ""; is_signed = false; tape_marks = false; } } // end of namespace dar-2.7.15/src/libdar/i_database.hpp0000644000175000017500000002000714636066467014127 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file i_database.hpp /// \brief this file holds the definition of class database implementation (pimpl idiom) /// \ingroup Private #ifndef I_DATABASE_HPP #define I_DATABASE_HPP #include "../my_config.h" #include #include "archive.hpp" #include "generic_file.hpp" #include "data_dir.hpp" #include "storage.hpp" #include "mem_ui.hpp" #include "database_options.hpp" #include "database_archives.hpp" #include "database.hpp" #include "tools.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the class i_database is the implementation of class database class database::i_database: public mem_ui { public: /// this constructor build an empty database i_database(const std::shared_ptr & dialog); /// this constructor reads i_database from a file i_database(const std::shared_ptr & dialog, const std::string & base, const database_open_options & opt); /// disabling copy constructor i_database(const i_database & ref) = delete; /// disabling move constructor i_database(i_database && ref) noexcept = delete; /// disabling assignement operator i_database & operator = (const i_database & ref) = delete; /// disabling move assignment operator i_database & operator = (i_database && ref) noexcept = delete; /// i_database destructor (no implicit file saving) ~i_database(); /// write the database to a file (see database_header first) void dump(const std::string & filename, const database_dump_options & opt) const; // SETTINGS /// add an archive to the database void add_archive(const archive & arch, const std::string & chemin, const std::string & basename, const database_add_options & opt); /// remove an archive from a database void remove_archive(archive_num min, archive_num max, const database_remove_options & opt); /// change order of archive within the database void set_permutation(archive_num src, archive_num dst); /// change one's archive basename recorded in the database void change_name(archive_num num, const std::string & basename, const database_change_basename_options &opt); /// change one's archive path recorded in the database void set_path(archive_num num, const std::string & chemin, const database_change_path_options & opt); /// change the default options given to dar when performing restoration void set_options(const std::vector &opt) { options_to_dar = opt; }; /// change the path to dar command void set_dar_path(const std::string & chemin) { dar_path = chemin; }; /// change compression to use when storing base on file void set_compression(compression algozip) const { algo = algozip; }; /// change the compression level to use when storing base in file void set_compression_level(U_I level) const { compr_level = level; }; //////////////////////// // // "GETTINGS" // /// provides the list of archive used to build the database database_archives_list get_contents() const; /// return the options used with dar for restoration std::vector get_options() const { return options_to_dar; }; // show option passed to dar /// returns the path for dar std::string get_dar_path() const { return dar_path; }; // show path to dar command /// returns the compression algorithm used on filesystem compression get_compression() const { return algo; }; /// returns the compression level used on file U_I get_compression_level() const { return compr_level; }; /// return the database format version std::string get_database_version() const { return tools_uint2str(cur_db_version); }; /// list files which are present in a given archive void get_files(database_listing_show_files_callback callback, void *context, archive_num num, const database_used_options & opt) const; /// list the archive where a give file is present void get_version(database_listing_get_version_callback callback, void *context, path chemin) const; /// compute some statistics about the location of most recent file versions void show_most_recent_stats(database_listing_statistics_callback callback, void *context) const; // "ACTIONS" (not available with partially extracted databases) /// restore files calling dar on the appropriated archive void restore(const std::vector & filename, const database_restore_options & opt); /// check that all files's Data and EA are more recent when archive number grows within the database, only warn the user bool check_order() const { bool initial_warn = true; if(files == nullptr) throw SRC_BUG; if(check_order_asked) return files->check_order(get_ui(), path("."), initial_warn) && initial_warn; else return true; } private: /// holds the archive used to create the database struct archive_data { std::string chemin; ///< path to the archive std::string basename; ///< basename of the archive datetime root_last_mod; ///< last modification date of the root directory }; std::deque coordinate; ///< list of archive used to build the database std::vector options_to_dar; ///< options to use when calling dar for restoration std::string dar_path; ///< path to dar data_dir *files; ///< structure containing files and their status in the set of archive used for that database (is set to nullptr in partial mode) storage *data_files; ///< when reading archive in partial mode, this is where is located the "not readed" part of the archive (is set to nullptr in partial-read-only mode) bool check_order_asked; ///< whether order check has been asked unsigned char cur_db_version; ///< current db version (for informational purposes) mutable compression algo; ///< compression used/to use when writing down the base to file mutable U_I compr_level; ///< the compression level to use void build(generic_file & f, bool partial, bool read_only, unsigned char db_version); ///< used by constructors archive_num get_real_archive_num(archive_num num, bool revert) const; const datetime & get_root_last_mod(const archive_num & num) const; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mycurl_slist.hpp0000644000175000017500000000527314636066467014614 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mycurl_slist.hpp /// \brief wrapper of the curl_slist struct to allow usual copy/move on C++ object /// \ingroup Private #ifndef MYCURL_SLIST_H #define MYCURL_SLIST_H #include "../my_config.h" extern "C" { #if LIBCURL_AVAILABLE #if HAVE_CURL_CURL_H #include #endif #endif } // end extern "C" #include #include namespace libdar { /// \addtogroup Private /// @{ #if LIBCURL_AVAILABLE /// wrapper class around curl_slist class mycurl_slist { public: mycurl_slist() { header = nullptr; }; mycurl_slist(const mycurl_slist & ref): appended(ref.appended) { header = rebuild(appended); }; mycurl_slist(mycurl_slist && ref) noexcept: appended(std::move(ref.appended)) { header = ref.header; ref.header = nullptr; }; mycurl_slist & operator = (const mycurl_slist & ref) { release(header); appended = ref.appended; header = rebuild(appended); return *this; }; mycurl_slist & operator = (mycurl_slist && ref) noexcept { std::swap(header, ref.header); std::swap(appended, ref.appended); return *this; }; ~mycurl_slist() { release(header); }; bool operator == (const mycurl_slist & ref) const; bool operator != (const mycurl_slist & ref) const { return ! (*this == ref); }; void append(const std::string & s); const curl_slist *get_address() const { return header; }; void clear() { release(header); appended.clear(); }; bool empty() const { return appended.empty(); }; private: struct curl_slist* header; std::deque appended; static curl_slist* rebuild(const std::deque & ap); static void release(curl_slist* & ptr) { curl_slist_free_all(ptr); ptr = nullptr; } }; #endif /// @} } // end of namespace #endif dar-2.7.15/src/libdar/i_libdar_xform.cpp0000644000175000017500000001546614636066467015043 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include #include #include "i_libdar_xform.hpp" #include "sar.hpp" #include "trivial_sar.hpp" #include "macro_tools.hpp" #include "tools.hpp" using namespace std; namespace libdar { libdar_xform::i_libdar_xform::i_libdar_xform(const shared_ptr & ui, const string & chem, const string & basename, const string & extension, const infinint & min_digits, const string & execute): mem_ui(ui) { sar *tmp_sar = nullptr; can_xform = true; init_entrep(); src_path.reset(new (nothrow) path(chem)); if(!src_path) throw Ememory("i_libdar_xform::lidar_xform"); entrep_src->set_location(*src_path); tmp_sar = new (nothrow) libdar::sar(get_pointer(), basename, extension, entrep_src, false, min_digits, false, false, execute); source.reset(tmp_sar); if(!source) throw Ememory("i_libdar_xform::lidar_xform"); if(tmp_sar == nullptr) throw SRC_BUG; else { // yes we modify directly the object // we assigned to "source", only for // short time and simplicity not to cast // back to libdar::sar type tmp_sar->set_info_status(CONTEXT_OP); format_07_compatible = tmp_sar->is_an_old_start_end_archive(); dataname = tmp_sar->get_data_name(); } } libdar_xform::i_libdar_xform::i_libdar_xform(const shared_ptr & ui, const std::string & pipename) : mem_ui(ui) { trivial_sar *tmp_sar = nullptr; can_xform = true; init_entrep(); tmp_sar = new (nothrow) libdar::trivial_sar(get_pointer(), pipename, false); source.reset(tmp_sar); if(!source) throw Ememory("i_libdar_xform::i_libdar_xform"); if(tmp_sar == nullptr) throw SRC_BUG; else { format_07_compatible = tmp_sar->is_an_old_start_end_archive(); dataname = tmp_sar->get_data_name(); } } libdar_xform::i_libdar_xform::i_libdar_xform(const shared_ptr & ui, int filedescriptor) : mem_ui(ui) { trivial_sar *tmp_sar = nullptr; can_xform = true; init_entrep(); tmp_sar = new (nothrow) libdar::trivial_sar(get_pointer(), filedescriptor, false); source.reset(tmp_sar); if(!source) throw Ememory("i_libdar_xform::i_libdar_xform"); if(tmp_sar == nullptr) throw SRC_BUG; else { format_07_compatible = tmp_sar->is_an_old_start_end_archive(); dataname = tmp_sar->get_data_name(); } } void libdar_xform::i_libdar_xform::xform_to(const string & chem, const string & basename, const string & extension, bool allow_over, bool warn_over, const infinint & pause, const infinint & first_slice_size, const infinint & slice_size, const string & slice_perm, const string & slice_user, const string & slice_group, hash_algo hash, const infinint & min_digits, const string & execute) { unique_ptr dst_path(new (nothrow) path(chem)); label internal_name; thread_cancellation thr; unique_ptr destination; bool force_perm = slice_perm != ""; U_I perm = force_perm ? tools_octal2int(slice_perm) : 0; if(!dst_path) throw Ememory("i_libdar_xform::xform_to"); entrep_dst->set_location(*dst_path); entrep_dst->set_user_ownership(slice_user); entrep_dst->set_group_ownership(slice_group); tools_avoid_slice_overwriting_regex(get_ui(), *entrep_dst, basename, extension, false, allow_over, warn_over, false); internal_name.generate_internal_filename(); thr.check_self_cancellation(); if(slice_size.is_zero()) // generating a single-sliced archive { destination.reset(new (nothrow) libdar::trivial_sar(get_pointer(), gf_write_only, basename, extension, *entrep_dst, internal_name, dataname, execute, allow_over, warn_over, force_perm, perm, hash, min_digits, format_07_compatible)); } else // generating multi-sliced archive { destination.reset(new (nothrow) libdar::sar(get_pointer(), gf_write_only, basename, extension, slice_size, first_slice_size, warn_over, allow_over, pause, entrep_dst, internal_name, dataname, force_perm, perm, hash, min_digits, format_07_compatible, execute)); } if(!destination) throw Ememory("i_libdar_xform::xform_to"); xform_to(destination.get()); } void libdar_xform::i_libdar_xform::xform_to(int filedescriptor, const string & execute) { label internal_name; unique_ptr destination; internal_name.generate_internal_filename(); destination.reset(macro_tools_open_archive_tuyau(get_pointer(), filedescriptor, gf_write_only, internal_name, dataname, format_07_compatible, execute)); if(!destination) throw Ememory("i_libdar_xform::xform_to"); xform_to(destination.get()); } void libdar_xform::i_libdar_xform::init_entrep() { entrep_src.reset(new (nothrow) entrepot_local("", "", false)); if(!entrep_src) throw Ememory("i_libdar_xform::lidar_xform"); entrep_dst.reset(new (nothrow) entrepot_local("", "", false)); if(!entrep_dst) throw Ememory("i_libdar_xform::lidar_xform"); } void libdar_xform::i_libdar_xform::xform_to(generic_file *dst) { if(dst == nullptr) throw SRC_BUG; try { source->copy_to(*dst); } catch(Escript & e) { throw; } catch(Euser_abort & e) { throw; } catch(Ebug & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { string msg = string(gettext("Error transforming the archive :"))+e.get_message(); throw Edata(msg); } } } // end of namespace dar-2.7.15/src/libdar/sparse_file.hpp0000644000175000017500000002101414636066467014346 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file sparse_file.hpp /// \brief class sparse_file definition, used to handle holes in files /// \ingroup Private /// /// this class is used to receive plain file's data to be written to the /// archive or to be read out from an archive. The class uses escape sequences /// to replace holes in files (long serie of zeros) by the number of zeros /// preceeded by a escape sequence mark. /// this class internally uses an escape object, with a modifed /// fixed escape sequence that optimizes the use of sparse_file objects with /// other escape objects. #ifndef SPARSE_FILE_HPP #define SPARSE_FILE_HPP #include "../my_config.h" extern "C" { #if HAVE_LIMITS_H #include #endif } #include "generic_file.hpp" #include "escape.hpp" #define SPARSE_FIXED_ZEROED_BLOCK 40960 #ifdef SSIZE_MAX #if SSIZE_MAX < MAX_BUFFER_SIZE #undef MAX_BUFFER_SIZE #define SPARSE_FIXED_ZEROED_BLOCK SSIZE_MAX #endif #endif namespace libdar { /// \addtogroup Private /// @{ class sparse_file : public escape { public: /// constructor /// \param[in] below object where to read or write data to or from /// \param[in] hole_size the size below which to ignore holes (zeroed bytes) /// this parameter is only used if "below" is in write-only mode sparse_file(generic_file *below, const infinint & hole_size = 15); sparse_file(const sparse_file & ref) = default; sparse_file(sparse_file && ref) noexcept = default; sparse_file & operator = (const sparse_file & ref) = default; sparse_file & operator = (sparse_file && ref) noexcept = default; ~sparse_file() = default; /// \note if set to true, inherited_write() call will not make any /// lookup for holes, the written data will simply be escaped if it /// could collide with a mark used to signal the start of a hole void write_as_escape(bool mode) { escape_write = mode; }; /// \note if set to true, the data will be unescaped or eof will /// be signaled to the first mark met, instead of interpreting the /// mark and what follows as a hole data structure. void read_as_escape(bool mode) { escape_read = mode; }; /// \note if set to true, the copy_to() methods, write zeroed data /// in place of skipping over a hole to restore it into the target /// generic_file void copy_to_without_skip(bool mode) { copy_to_no_skip = mode; }; bool has_seen_hole() const { return seen_hole; }; bool has_escaped_data() const { return data_escaped; }; /// copies data of the current object using holes to the given /// generic_file, overwrites the generic_file method /// \note, this assumes the underlying generic_file (where the sparse_file object /// reads its data before writing it to "ref") uses a special /// datastructure (which relies on class escape). This datastructure /// is created by sparse_file in write_only mode (thus when one is writing /// data to a sparse_file object, the underlying generic_file get a mix of /// normal data and hole (number of zeroed bytes to skip to reach next normal data) /// leading to only restore the data not equal to zero (passed a certain /// amount of contiguous zeroed bytes). virtual void copy_to(generic_file & ref) override { crc *tmp = nullptr; copy_to(ref, 0, tmp); if(tmp != nullptr) throw SRC_BUG; }; /// same as sparse_file::copy_to(generic_file) just above but here with crc, /// \note this also overwrite the corresponding class generic_file method, as we need a specific implementation here virtual void copy_to(generic_file & ref, const infinint & crc_size, crc * & value) override; // indirectly inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override { return false; }; virtual bool skip(const infinint & pos) override { if(pos != offset) throw Efeature("skip in sparse_file"); else return true; }; virtual bool skip_to_eof() override { throw Efeature("skip in sparse_file"); }; virtual bool skip_relative(S_I x) override { if(x != 0) throw Efeature("skip in sparse_file"); return true; }; virtual infinint get_position() const override; protected: // methods from the escape class we hide from the (public) class interface void add_mark_at_current_position(sequence_type t) { escape::add_mark_at_current_position(t); }; bool skip_to_next_mark(sequence_type t, bool jump) { return escape::skip_to_next_mark(t, jump); }; bool next_to_read_is_mark(sequence_type t) { return escape::next_to_read_is_mark(t); }; void add_unjumpable_mark(sequence_type t) { escape::add_unjumpable_mark(t); }; // methods from generic_file redefined as protected virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_sync_write() override; // inherited_flush_read() kept as is from the escape class // inherited_terminate() kept as is from the escape class private: static bool initialized; ///< whether static field "zeroed_field" has been initialized static unsigned char zeroed_field[SPARSE_FIXED_ZEROED_BLOCK]; ///< read-only, used when the sequence of zeros is too short for a hole enum { normal, hole } mode; ///< wether we are currently reading/writing a hole or normal data infinint zero_count; ///< number of zeroed byte pending in the current hole infinint offset; ///< current offset in file (as if it was a plain file). infinint min_hole_size; ///< minimum size of hole to consider U_I UI_min_hole_size; ///< if possible store min_hole_size as U_I, if not this field is set to zero which disables the hole lookup inside buffers while writing data bool escape_write; ///< whether to behave like an escape object when writing down data bool escape_read; ///< whether to behave like an escape object when reading out data bool copy_to_no_skip; ///< whether to hide holes by zeored bytes in the copy_to() methods bool seen_hole; ///< whether a hole has been seen or this is a plain file so far bool data_escaped; ///< whether some data has been escaped to not collide with a mark (may occur even when no hole is met) /// write down the amount of byte zero not yet written. /// which may be normal zeros or hole depending on their amount void dump_pending_zeros(); /// write a hole datastructure void write_hole(const infinint & length); /// reset the sparse_file detection as if "this" object was just created /// /// \note may lead the offset to be backward it previous position void reset(); /// analyse a buffer for a hole /// \param[in] a pointer to the buffer area /// \param[in] size size of the buffer to inspect /// \param[in] min_hole_size minimum size of hole to consider, if set to zero only consider hole at end of buffer /// \param[out] start in "a" where starts the found hole /// \param[out] length length of the hole in byte /// \return true if a hole has been found, false else /// \note if the buffer ends by zeros, start points to the first zero, and length may be less than min_hole_size static bool look_for_hole(const char *a, U_I size, U_I min_hole_size, U_I & start, U_I & length); /// count the number of zeroed byte starting at the provided buffer /// \param[in] a a pointer to the buffer area /// \param[in] size the size of the buffer to inspect /// \return the number of zeroed bytes found at the beginning of the buffer static U_I count_initial_zeros(const char *a, U_I size); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/database5.hpp0000644000175000017500000001311714636067146013703 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file database5.hpp /// \brief this file holds the database class definition as defined in API version 5 /// \ingroup API5 #ifndef DATABASE5_HPP #define DATABASE5_HPP #include "../my_config.h" #include #include "archive.hpp" #include "generic_file.hpp" #include "data_tree.hpp" #include "storage.hpp" #include "database_options.hpp" #include "database.hpp" #include "user_interaction5.hpp" #include "data_tree.hpp" #include "path.hpp" #include "database_listing_callback.hpp" #include "database_aux.hpp" #include "datetime.hpp" #include "user_interaction_blind.hpp" // from path.hpp using libdar::path; // from database_options.hpp using libdar::database_open_options; using libdar::database_dump_options; using libdar::database_add_options; using libdar::database_remove_options; using libdar::database_change_basename_options; using libdar::database_change_path_options; using libdar::database_restore_options; using libdar::database_used_options; // from database_aux.hpp using libdar::archive_num; using libdar::db_lookup; using libdar::db_etat; // from datetime.hpp using libdar::datetime; namespace libdar5 { /// \addtogroup API5 /// @{ /// the database class defines the dar_manager database /// all operations for a dar_manager database are defines through the /// use of this class interface. This class also defines internally /// the data structure of the database. class database: public libdar::database { public: database(): libdar::database(std::shared_ptr(new libdar::user_interaction_blind())) {}; database(user_interaction & dialog, const std::string & base, const database_open_options & opt): libdar::database(user_interaction5_clone_to_shared_ptr(dialog), base, opt) {} /// disabling copy constructor database(const database & ref) = delete; /// disabling move constructor database(database && ref) noexcept = delete; /// disabling assignement operator database & operator = (const database & ref) = delete; /// disabling move assignment operator database & operator = (database && ref) noexcept = delete; /// database destructor (no implicit file saving) ~database() = default; void dump(user_interaction & dialog, const std::string & filename, const database_dump_options & opt) const { libdar::database::dump(filename, opt); } // "GETTINGS" /// show the list of archive used to build the database /// \param[in,out] dialog is the user_interaction to use to report the listing void show_contents(user_interaction & dialog) const; // displays all archive information /// list files which are present in a given archive /// \param[in,out] dialog where to display listing to /// \param[in] num is the archive number to look at /// \param[in] opt optional parameters for this operation /// \note if "num" is set to zero all archive contents is listed /// \note this method is not available with partially extracted databases. void show_files(user_interaction & dialog, archive_num num, const database_used_options & opt) const; /// list the archive where a give file is present /// \param[in,out] dialog where to display the listing to /// \param[in] chemin path to the file to look for /// \note this method is not available with partially extracted databases. void show_version(user_interaction & dialog, path chemin) const; /// compute some statistics about the location of most recent file versions /// \param[in] dialog where to display the listing to /// \note this method is not available with partially extracted databases. void show_most_recent_stats(user_interaction & dialog) const; void restore(user_interaction & dialog, const std::vector & filename, const database_restore_options & opt) { libdar::database::restore(filename, opt); } bool check_order(user_interaction & dialog) const { return libdar::database::check_order(); } private: static void show_files_callback(void *tag, const std::string & filename, bool available_data, bool available_ea); static void get_version_callback(void *tag, archive_num num, db_etat data_presence, bool has_data_date, datetime data, db_etat ea_presence, bool has_ea_date, datetime ea); static void statistics_callback(void *tag, U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/datetime.cpp0000644000175000017500000002376114636067146013647 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "datetime.hpp" #include "archive_version.hpp" #include "generic_file.hpp" using namespace std; namespace libdar { static const infinint one_unit = 1; static const infinint one_thousand = 1000; static const infinint one_million = one_thousand*one_thousand; static const infinint one_billion = one_million*one_thousand; datetime::datetime(time_t second, time_t subsec, time_unit unit) { build(infinint(second), infinint(subsec), unit); } datetime::datetime(generic_file &x, archive_version ver) { read(x, ver); } bool datetime::operator < (const datetime & ref) const { if(uni <= ref.uni && val < ref.val) return true; if(uni < ref.uni) { infinint newval, reste; euclide(val, get_scaling_factor(ref.uni, uni), newval, reste); return newval < ref.val; } if(uni == ref.uni) return val < ref.val; else { // uni > ref.uni infinint newval, reste; euclide(ref.val, get_scaling_factor(uni, ref.uni), newval, reste); return (val == newval && !reste.is_zero()) || val < newval; } } bool datetime::operator == (const datetime & ref) const { return uni == ref.uni && val == ref.val; // fields are always reduced to the larger possible unit } void datetime::operator -= (const datetime & ref) { if(ref.uni < uni) { val *= get_scaling_factor(uni, ref.uni); uni = ref.uni; } if(ref.uni == uni) { if(val < ref.val) throw SRC_BUG; val -= ref.val; } else // ref.uni > uni { infinint tmp = ref.val * get_scaling_factor(ref.uni, uni); if(tmp > val) throw SRC_BUG; val -= tmp; } reduce_to_largest_unit(); } void datetime::operator += (const datetime & ref) { if(ref.uni < uni) { val *= get_scaling_factor(uni, ref.uni); uni = ref.uni; } if(ref.uni == uni) val += ref.val; else // ref.uni > uni { infinint tmp = ref.val * get_scaling_factor(ref.uni, uni); val += tmp; } reduce_to_largest_unit(); } bool datetime::loose_equal(const datetime & ref) const { if(uni == ref.uni) return val == ref.val; else { time_unit tu = max(uni, ref.uni); infinint val1, val2; if(uni < tu) val1 = val / get_scaling_factor(tu, uni); else val1 = val; if(ref.uni < tu) val2 = ref.val / get_scaling_factor(tu, ref.uni); else val2 = ref.val; return val1 == val2; } } datetime datetime::loose_diff(const datetime & ref) const { #if LIBDAR_TIME_READ_ACCURACY >= LIBDAR_TIME_ACCURACY_NANOSECOND && LIBDAR_TIME_WRITE_ACCURACY >= LIBDAR_TIME_ACCURACY_NANOSECOND static const time_unit max_capa = tu_nanosecond; #else #if LIBDAR_TIME_READ_ACCURACY >= LIBDAR_TIME_ACCURACY_MICROSECOND && LIBDAR_TIME_WRITE_ACCURACY >= LIBDAR_TIME_ACCURACY_MICROSECOND static const time_unit max_capa = tu_microsecond; #else static const time_unit max_capa = tu_second; #endif #endif datetime ret; infinint aux; // using the less precised unit to avoid loosing accuracy ret.uni = max(uni, ref.uni); if(ret.uni < max_capa) ret.uni = max_capa; if(uni < ret.uni) ret.val = val / get_scaling_factor(ret.uni, uni); else ret.val = val; if(ref.uni < ret.uni) aux = ref.val / get_scaling_factor(ret.uni, ref.uni); else aux = ref.val; if(ret.val < aux) throw SRC_BUG; // negative date would result of the operation ret.val -= aux; ret.reduce_to_largest_unit(); return ret; } infinint datetime::get_storage_size() const { infinint sec, sub, size; get_value(sec, sub, uni); size = sec.get_storage_size(); if(uni < tu_second) size += sub.get_storage_size() + 1; return size; } void datetime::reduce_to_largest_unit() const { infinint newval, reste; datetime *me = const_cast(this); if(me == nullptr) throw SRC_BUG; if(val.is_zero()) { if(uni != tu_second) me->uni = tu_second; } else { switch(uni) { case tu_nanosecond: euclide(val, get_scaling_factor(tu_microsecond, uni), newval, reste); if(!reste.is_zero()) break; // cannot reduce the unit further else { me->val = newval; me->uni = tu_microsecond; } /* no break ! */ case tu_microsecond: euclide(val, get_scaling_factor(tu_second, uni), newval, reste); if(!reste.is_zero()) break; // cannot reduce the unit further else { me->val = newval; me->uni = tu_second; } /* no break ! */ case tu_second: // cannot reduce further as // this is the largest known time unit // so we break here break; default: throw SRC_BUG; } } } void datetime::get_value(infinint & sec, infinint & sub, time_unit unit) const { euclide(val, get_scaling_factor(tu_second, uni), sec, sub); if(unit < uni) sub *= get_scaling_factor(uni, unit); if(unit > uni) sub /= get_scaling_factor(unit, uni); } void datetime::build(const infinint & sec, const infinint & sub, time_unit unit) { bool loop = false; infinint subsec = sub; do { try { if(tu_second == unit) val = sec; // this saves an infinint multiplication and fixes // a bug when reading an archive generated by dar 2.4.x or // below that included a file for which the system returned // a negative date, which was read by libdar as a huge positive // number, that much that multiplying it by 1 triggers the // limiting overflow mecanism else val = sec*get_scaling_factor(tu_second, unit) + subsec; uni = unit; loop = false; } catch(Elimitint& e) { switch(unit) { case tu_nanosecond: unit = tu_microsecond; subsec = subsec/1000; break; case tu_microsecond: unit = tu_second; subsec = subsec/1000; break; case tu_second: throw; default: throw SRC_BUG; } loop = true; } } while(loop); reduce_to_largest_unit(); } infinint datetime::get_subsecond_value(time_unit unit) const { infinint ret, tmp; get_value(tmp, ret, unit); return ret; } bool datetime::get_value(time_t & second, time_t & subsecond, time_unit unit) const { infinint sub, sec; get_value(sec, sub, unit); second = 0; sec.unstack(second); if(!sec.is_zero()) return false; subsecond = 0; sub.unstack(subsecond); if(!sub.is_zero()) return false; return true; } void datetime::dump(generic_file &x) const { char tmp; infinint sec, sub; get_value(sec, sub, uni); tmp = time_unit_to_char(uni); // we keep storing: // - a first flag telling the unit // - an infinint telling the amount of seconds // - an other infinint telling the amount of subsecond additional time expressed in the unit of the flag x.write(&tmp, 1); sec.dump(x); if(uni < tu_second) sub.dump(x); } void datetime::read(generic_file &f, archive_version ver) { infinint sec, sub; if(ver < 9) uni = tu_second; else { char tmp; f.read(&tmp, 1); uni = char_to_time_unit(tmp); } sec.read(f); if(uni < tu_second) sub.read(f); else sub = 0; build(sec, sub, uni); } datetime::time_unit datetime::min(time_unit a, time_unit b) { if(a < b) return a; else return b; } datetime::time_unit datetime::max(time_unit a, time_unit b) { if(a < b) return b; else return a; } char datetime::time_unit_to_char(time_unit a) { switch(a) { case tu_nanosecond: return 'n'; case tu_microsecond: return 'u'; case tu_second: return 's'; default: throw SRC_BUG; } } datetime::time_unit datetime::char_to_time_unit(const char a) { switch(a) { case 'n': return tu_nanosecond; case 's': return tu_second; case 'u': return tu_microsecond; default: throw Erange("datetime::time_unit", gettext("Unknown time unit")); } } const infinint & datetime::get_scaling_factor(time_unit source, time_unit dest) { if(dest > source) throw SRC_BUG; switch(source) { case tu_second: if(dest == tu_second) return one_unit; else if(dest == tu_microsecond) return one_million; else if(dest == tu_nanosecond) return one_billion; else throw SRC_BUG; // unknown dest unit! case tu_microsecond: if(dest == tu_microsecond) return one_unit; else if(dest == tu_nanosecond) return one_thousand; else throw SRC_BUG; // unknown dest unit! case tu_nanosecond: if(dest == tu_nanosecond) return one_unit; else throw SRC_BUG; // unknown dest unit! default: throw SRC_BUG; } } archive_version db2archive_version(unsigned char db_version) { // the class datetime read() method is based on dar archive version. // here we know the database version (dar_manager). Starting with version 4 (release 2.5.0) // time is no more stored as an integer. This correspond to dar archive version 9 // and above (release 2.5.0 too), wherefrom this hack below: return db_version > 3 ? archive_version(9,0) : archive_version(8,0); } } // end of namespace dar-2.7.15/src/libdar/crit_action.cpp0000644000175000017500000001133514636066467014350 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "crit_action.hpp" #include "nls_swap.hpp" #include "cat_all_entrees.hpp" #include "tools.hpp" #include "cat_nomme.hpp" #include "op_tools.hpp" using namespace std; namespace libdar { testing::testing(const criterium & input, const crit_action & go_true, const crit_action & go_false) { x_input = input.clone(); x_go_true = go_true.clone(); x_go_false = go_false.clone(); if(!check()) { free(); throw Ememory("testing::testing"); } } void testing::free() noexcept { if(x_input != nullptr) { delete x_input; x_input = nullptr; } if(x_go_true != nullptr) { delete x_go_true; x_go_true = nullptr; } if(x_go_false != nullptr) { delete x_go_false; x_go_false = nullptr; } } void testing::copy_from(const testing & ref) { x_input = ref.x_input->clone(); x_go_true = ref.x_go_true->clone(); x_go_false = ref.x_go_false->clone(); if(!check()) { free(); throw Ememory("testing::copy_from"); } } void testing::move_from(testing && ref) noexcept { swap(x_input, ref.x_input); swap(x_go_true, ref.x_go_true); swap(x_go_false, ref.x_go_false); } bool testing::check() const { return x_input != nullptr && x_go_true != nullptr && x_go_false != nullptr; } void crit_chain::add(const crit_action & act) { crit_action *tmp = act.clone(); if(tmp == nullptr) throw Ememory("crit_chain::add"); sequence.push_back(tmp); } void crit_chain::gobe(crit_chain & to_be_voided) { deque::iterator it = to_be_voided.sequence.begin(); try { while(it != to_be_voided.sequence.end()) { if(*it == nullptr) throw SRC_BUG; sequence.push_back(*it); ++it; } to_be_voided.sequence.clear(); } catch(...) { if(it != to_be_voided.sequence.end() && sequence.back() == *it) ++it; while(it != to_be_voided.sequence.end()) { if(*it != nullptr) { delete *it; *it = nullptr; } ++it; } to_be_voided.sequence.clear(); throw; } } void crit_chain::get_action(const cat_nomme & first, const cat_nomme & second, over_action_data & data, over_action_ea & ea) const { NLS_SWAP_IN; try { deque::const_iterator it = sequence.begin(); over_action_data tmp_data; over_action_ea tmp_ea; data = data_undefined; ea = EA_undefined; if(it == sequence.end()) throw Erange("crit_chain::get_action", gettext("cannot evaluate an empty chain in an overwriting policy")); while(it != sequence.end() && (data == data_undefined || ea == EA_undefined)) { if(*it == nullptr) throw SRC_BUG; (*it)->get_action(first, second, tmp_data, tmp_ea); if(data == data_undefined || tmp_data != data_undefined) data = tmp_data; if(ea == EA_undefined || tmp_ea != EA_undefined) ea = tmp_ea; ++it; } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void crit_chain::destroy() { deque::iterator it = sequence.begin(); while(it != sequence.end()) { if(*it != nullptr) { delete *it; *it = nullptr; } ++it; } sequence.clear(); } void crit_chain::copy_from(const crit_chain & ref) { deque::const_iterator it = ref.sequence.begin(); crit_action * tmp = nullptr; sequence.clear(); try { while(it != ref.sequence.end()) { if(*it == nullptr) throw SRC_BUG; tmp = (*it)->clone(); if(tmp == nullptr) throw Ememory("crit_chain::copy_from"); sequence.push_back(tmp); tmp = nullptr; ++it; } } catch(...) { destroy(); if(tmp != nullptr) delete tmp; throw; } } } // end of namespace dar-2.7.15/src/libdar/crypto_module.hpp0000644000175000017500000001130114636066467014735 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file crypto_module.hpp /// \brief per block cryptography implementation /// \ingroup Private /// /// used for strong encryption. #ifndef CRYPTO_MODULE_HPP #define CRYPTO_MODULE_HPP #include "../my_config.h" #include #include #include "infinint.hpp" #include "integers.hpp" namespace libdar { /// \addtogroup Private /// @{ class crypto_module { public: crypto_module() {}; crypto_module(const crypto_module & ref) = default; crypto_module(crypto_module && ref) noexcept = default; crypto_module & operator = (const crypto_module & ref) = default; crypto_module & operator = (crypto_module && ref) noexcept = default; virtual ~crypto_module() noexcept = default; /// defines the size necessary to encrypt a given amount of clear data /// \param[in] clear_block_size is the size of the clear block to encrypt. /// \return the size of the memory to allocate to receive corresponding encrypted data. /// \note this implies that encryption algorithm must always generate a fixed size encrypted block of data for /// a given fixed size block of data. However, the size of the encrypted block of data may differ from /// the size of the clear block of data virtual U_32 encrypted_block_size_for(U_32 clear_block_size) = 0; /// it may be necessary by the inherited class have few more bytes allocated after the clear data given for encryption /// \param[in] clear_block_size is the size in byte of the clear data that will be asked to encrypt. /// \return the requested allocated buffer size (at least the size of the clear data). /// \note when giving clear buffer of data of size "clear_block_size" some inherited class may requested /// that a bit more of data must be allocated. /// this is to avoid copying data when the algorithm needs to add some data after the /// clear data before encryption. virtual U_32 clear_block_allocated_size_for(U_32 clear_block_size) = 0; /// this method encrypts the clear data given /// \param block_num is the number of the block to which correspond the given data, This is an informational field for inherited classes. /// \param[in] clear_buf points to the first byte of clear data to encrypt. /// \param[in] clear_size is the length in byte of data to encrypt. /// \param[in] clear_allocated is the size of the allocated memory (modifiable bytes) in clear_buf: clear_block_allocated_size_for(clear_size) /// \param[in,out] crypt_buf is the area where to put corresponding encrypted data. /// \param[in] crypt_size is the allocated memory size for crypt_buf: encrypted_block_size_for(clear_size) /// \return is the amount of data put in crypt_buf (<= crypt_size). /// \note it must respect that : returned value = encrypted_block_size_for(clear_size argument) virtual U_32 encrypt_data(const infinint & block_num, const char *clear_buf, const U_32 clear_size, const U_32 clear_allocated, char *crypt_buf, U_32 crypt_size) = 0; /// this method decyphers data /// \param[in] block_num block number of the data to decrypt. /// \param[in] crypt_buf pointer to the first byte of encrypted data. /// \param[in] crypt_size size of encrypted data to decrypt. /// \param[in,out] clear_buf pointer where to put clear data. /// \param[in] clear_size allocated size of clear_buf. /// \return is the amount of data put in clear_buf (<= clear_size) virtual U_32 decrypt_data(const infinint & block_num, const char *crypt_buf, const U_32 crypt_size, char *clear_buf, U_32 clear_size) = 0; virtual std::unique_ptr clone() const = 0; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filtre.hpp0000644000175000017500000002311614636067146013337 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filtre.hpp /// \brief here is all the core routines for the operations /// \ingroup Private #ifndef FILTRE_HPP #define FILTRE_HPP #include "../my_config.h" #include "mask.hpp" #include "pile.hpp" #include "catalogue.hpp" #include "path.hpp" #include "statistics.hpp" #include "archive_options.hpp" #include "thread_cancellation.hpp" #include "filesystem_ids.hpp" namespace libdar { /// \addtogroup Private /// @{ extern void filtre_restore(const std::shared_ptr & dialog, ///< for user interaction const mask &filtre, ///< which filename to restore const mask & subtree, ///< which directory and paths to restore const catalogue & cat, ///< table of content to extract information from const path & fs_racine, ///< root path under which to restore directiry tree and files bool fs_warn_overwrite, ///< whether to warn before overwriting (to be replaced by overwriting policy) bool info_details, ///< whether to display processing messages bool display_treated, ///< whether to display treated files bool display_treated_only_dir, ///< whether to only display current directory of treated file bool display_skipped, ///< whether to display skipped files statistics & st, ///< statistics result about the operation const mask & ea_mask, ///< defines EA to restore/not restore bool flat, ///< if true, directories are not restores, all files are placed directly at in fs_racine directory comparison_fields what_to_check, ///< which file properties to restore bool warn_remove_no_match, ///< wether to warn for file to remove not matching the expected type bool empty, ///< dry-run execution bool empty_dir, ///< whether to restore directories that do contain any file to restore const crit_action & x_overwrite, ///< how and whether to overwrite files archive_options_extract::t_dirty dirty, ///< whether to restore dirty files bool only_deleted, ///< whether to only consider deleted files bool not_deleted, ///< wether to consider deleted files const fsa_scope & scope, ///< scope of FSA to take into account bool ignore_unix_sockets ///< do not try to restore unix sockets ); extern void filtre_sauvegarde(const std::shared_ptr & dialog, 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_time, bool furtive_read_mode, const filesystem_ids & same_fs, 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 std::string & backup_hook_file_execute, const mask & backup_hook_file_mask, bool ignore_unknown, const fsa_scope & scope, const std::string & exclude_by_ea, bool delta_signature, // whether to compute delta sig file on the saved file const infinint & delta_sig_min_size, // size below which to never calculate delta sig const mask & delta_mask, // mask defining for which file to calculate delta sig bool delta_diff, // whether to perform delta diff backup when delta sig is present bool auto_zeroing_neg_dates, const std::set & ignored_symlinks, modified_data_detection mod_data_detect, const delta_sig_block_size & delta_sig_block_len); extern void filtre_difference(const std::shared_ptr & dialog, 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_time, bool furtive_read_mode, comparison_fields what_to_check, const infinint & hourshift, bool compare_symlink_date, const fsa_scope & scope, bool isolated_mode); extern void filtre_test(const std::shared_ptr & dialog, 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); extern void filtre_merge(const std::shared_ptr & dialog, 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 & overwrite, bool warn_overwrite, bool decremental_mode, const infinint & sparse_file_min_size, const fsa_scope & scope, bool delta_signature, bool build_delta_sig, const infinint & delta_sig_min_size, const mask & delta_mask, const delta_sig_block_size & signature_block_size); /// initialize variables used for merging in step1 and step2 /// \note also used for repairing extern void filtre_merge_step0(const std::shared_ptr & dialog, const catalogue * ref1, const catalogue * ref2, statistics & st, bool decremental_mode, crit_action* & decr, const crit_action* & overwrite, bool & abort, thread_cancellation & thr_cancel); /// builds a catalogue from two refs with the given policy and filters extern void filtre_merge_step1(const std::shared_ptr & dialog, const mask & filtre, const mask & subtree, catalogue & cat, const catalogue * ref1, const catalogue * ref2, bool info_details, bool display_treated, bool display_skipped, statistics & st, bool make_empty_dir, bool warn_overwrite, bool decremental_mode, crit_action* & decr, const crit_action* & overwrite, bool & abort, thread_cancellation & thr_cancel); /// copies data of "cat" catalogue to the pdesc of a brand new archive /// \note also used for repairing extern void filtre_merge_step2(const std::shared_ptr & dialog, const pile_descriptor & pdesc, catalogue & cat, bool info_details, bool display_treated, bool display_treated_only_dir, const mask & compr_mask, const infinint & min_compr_size, bool keep_compressed, const infinint & sparse_file_min_size, bool delta_signature, bool build_delta_sig, const infinint & delta_sig_min_size, const mask & delta_mask, bool & abort, thread_cancellation & thr_cancel, bool repair_mode, const delta_sig_block_size & signature_block_size); void filtre_sequentially_read_all_catalogue(catalogue & cat, const std::shared_ptr & dialog, bool lax_read_mode); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/compress_block_header.hpp0000644000175000017500000000403514636066467016373 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file compress_block_header.hpp /// \brief block header used for compression per block /// \ingroup Private #ifndef COMPRESS_BLOCK_HEADER_HPP #define COMPRESS_BLOCK_HEADER_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// block header structure used for storing compressed blocks struct compress_block_header { // type values static constexpr const char H_DATA = 1; static constexpr const char H_EOF = 2; // fields char type; ///< let the possibility to extend this architecture (for now type is fixed) infinint size; ///< size of the following compressed block of data void dump(generic_file & f); /// read the block header from a generic_file (an archive) /// \return true of a whole block header could be read. /// False is returned only if no data could be read at all (eof) /// else an exception is thrown bool set_from(generic_file & f); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/ea.hpp0000644000175000017500000000617414636067146012444 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file ea.hpp /// \brief contains a set of routines to manage EA values associated to a file /// \ingroup Private /// \note API included module due to dependencies #ifndef EA_HPP #define EA_HPP #include "../my_config.h" #include #include #include "infinint.hpp" #include "mask.hpp" namespace libdar { /// \addtogroup Private /// @{ /// non need to to dig into this class from API class archive_version; class generic_file; /// the class ea_attributs manages the set of EA that can be associated to an inode class ea_attributs { public: ea_attributs() { alire = attr.begin(); }; ea_attributs(generic_file & f, const archive_version & edit); ea_attributs(const ea_attributs & ref); ea_attributs(ea_attributs && ref) noexcept; ea_attributs & operator = (const ea_attributs & ref); ea_attributs & operator = (ea_attributs && ref) noexcept; ~ea_attributs() = default; bool operator == (const ea_attributs & ref) const { return attr == ref.attr; }; void dump(generic_file & f) const; void add(const std::string & key, const std::string & value) { attr[key] = value; }; void reset_read() const; bool read(std::string & key, std::string & value) const; infinint size() const { return attr.size(); }; void clear() { attr.clear(); alire = attr.begin(); }; bool find(const std::string &key, std::string & found_value) const; bool diff(const ea_attributs & other, const mask & filter) const; infinint space_used() const; /// addition operator. /// \param[in] arg ea_attributs to add to self /// \return a ea_attributs object containing all EA of the current object enriched and possibly overwritten /// by those of "arg". /// \note this operator is not reflexive (or symetrical if you prefer) unlike it is in arithmetic. Here instead /// "a + b" is possibly not equal to "b + a" ea_attributs operator + (const ea_attributs & arg) const; private: std::map attr; mutable std::map::const_iterator alire; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_directory.cpp0000644000175000017500000005422714636067146014707 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_all_entrees.hpp" #include "tools.hpp" using namespace std; namespace libdar { // static field of class cat_directory const cat_eod cat_directory::fin; // methods of class cat_directory cat_directory::cat_directory(const infinint & xuid, const infinint & xgid, U_16 xperm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const string & xname, const infinint & fs_device) : cat_inode(xuid, xgid, xperm, last_access, last_modif, last_change, xname, fs_device) { parent = nullptr; #ifdef LIBDAR_FAST_DIR fils.clear(); #endif ordered_fils.clear(); it = ordered_fils.begin(); set_saved_status(saved_status::saved); recursive_has_changed = true; updated_sizes = false; } cat_directory::cat_directory(const cat_directory & ref) : cat_inode(ref) { init(); recursive_has_changed = ref.recursive_has_changed; } cat_directory::cat_directory(cat_directory && ref) noexcept: cat_inode(move(ref)) { init(); recursive_has_changed = move(ref.recursive_has_changed); } cat_directory & cat_directory::operator = (const cat_directory & ref) { // this assigns the inode part of the object // we don't modify the existing subfiles or subdirectories nor we copy them from the reference cat_directory cat_inode::operator = (ref); recursive_flag_size_to_update(); return *this; } cat_directory & cat_directory::operator = (cat_directory && ref) { // this assigns the inode part of the object // we don't modify the existing subfiles or subdirectories nor we copy them from the reference cat_directory // to stay coherent with the copy_from operation cat_inode::operator = (move(ref)); recursive_flag_size_to_update(); return *this; } cat_directory::cat_directory(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, entree_stats & stats, std::map & corres, compression default_algo, bool lax, bool only_detruit, bool small) : cat_inode(dialog, pdesc, reading_ver, saved, small) { cat_entree *p; cat_nomme *t; cat_directory *d; cat_detruit *x; cat_mirage *m; cat_eod *fin = nullptr; bool lax_end = false; parent = nullptr; #ifdef LIBDAR_FAST_DIR fils.clear(); #endif ordered_fils.clear(); recursive_has_changed = true; // need to call recursive_has_changed_update() first if this fields has to be used updated_sizes = false; if(only_detruit) { // only detruit is used in sequential read mode // when the in-lined metadata has been completely // read. Then catalogue at end of file is read // using "only_detruit" to build a directory tree // only containing the detruit objects (file removed // since backup of reference was made). // // if sequential read mode is used over a pipe // we must drop the EA and FSA of the directories, // to avoid having the calling filtre_restore routine // to try restoring them. First they have already been // restored during the in-lined data/metadata reading // of the backup and doing so here would lead the // fitre_restore to ask to skip backward in the archive // to fetch EA and FSA // // if the underlying structure is a pipe, this will // fail, leading dar warning that skipping backward // on a pipe is not possible. // // for that reason, in "only_detruit" mode the catalogue // need to drop all EA and FSA of its directories // this is what is done here: if(ea_get_saved_status() == ea_saved_status::full) ea_set_saved_status(ea_saved_status::partial); if(fsa_get_saved_status() == fsa_saved_status::full) fsa_set_saved_status(fsa_saved_status::partial); } try { while(fin == nullptr && !lax_end) { try { p = cat_entree::read(dialog, pdesc, reading_ver, stats, corres, default_algo, lax, only_detruit, small); } catch(Euser_abort & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { if(!lax) throw; else { dialog->message(string(gettext("LAX MODE: Error met building a catalogue entry, skipping this entry and continuing. Skipped error is: ")) + e.get_message()); p = nullptr; } } if(p != nullptr) { d = dynamic_cast(p); fin = dynamic_cast(p); t = dynamic_cast(p); x = dynamic_cast(p); m = dynamic_cast(p); if(!only_detruit || d != nullptr || x != nullptr || fin != nullptr || m != nullptr) { // we must add the cat_mirage object, else // we will trigger an incoherent catalogue structure // as the cat_mirage without inode cannot link to the cat_mirage with inode // carring the same etiquette if we destroy them right now. if(t != nullptr) // p is a "cat_nomme" { #ifdef LIBDAR_FAST_DIR fils[t->get_name()] = t; #endif ordered_fils.push_back(t); } if(d != nullptr) // p is a cat_directory d->parent = this; if(t == nullptr && fin == nullptr) throw SRC_BUG; // neither an cat_eod nor a cat_nomme ! what's that ??? } else { delete p; p = nullptr; d = nullptr; fin = nullptr; t = nullptr; x = nullptr; } } else if(!lax) throw Erange("cat_directory::cat_directory", gettext("missing data to build a cat_directory")); else lax_end = true; } if(fin != nullptr) { delete fin; // no need to keep it fin = nullptr; } it = ordered_fils.begin(); } catch(Egeneric & e) { clear(); throw; } } cat_directory::~cat_directory() noexcept(false) { clear(); } bool cat_directory::operator == (const cat_entree & ref) const { const cat_directory *ref_dir = dynamic_cast(&ref); if(ref_dir == nullptr) return false; else return cat_inode::operator == (ref); } void cat_directory::inherited_dump(const pile_descriptor & pdesc, bool small) const { deque::const_iterator x = ordered_fils.begin(); cat_inode::inherited_dump(pdesc, small); if(!small) { while(x != ordered_fils.end()) { if(*x == nullptr) throw SRC_BUG; if(dynamic_cast(*x) != nullptr) ++x; // "cat_ignored" need not to be saved, they are only useful when updating_destroyed else { (*x)->specific_dump(pdesc, small); ++x; } } } // else in small mode, we do not dump any children // an inode may have children while small dump is asked // when performing a merging operation fin.specific_dump(pdesc, small); // end of "this" cat_directory // fin is a static constant variable of class cat_directory, // this hack avoids recurrent construction/destruction of a cat_eod object. } void cat_directory::recursive_update_sizes() const { if(!updated_sizes) { x_size = 0; x_storage_size = 0; deque::const_iterator it = ordered_fils.begin(); const cat_directory *f_dir = nullptr; const cat_file *f_file = nullptr; while(it != ordered_fils.end()) { if(*it == nullptr) throw SRC_BUG; f_dir = dynamic_cast(*it); f_file = dynamic_cast(*it); if(f_dir != nullptr) { // recursion occurs here // by calling get_size() and get_storage_size() of child directories /// which in turn will call the recursive_update_sizes() of child objects x_size += f_dir->get_size(); x_storage_size += f_dir->get_storage_size(); } else if(f_file != nullptr && (f_file->get_saved_status() == saved_status::saved || f_file->get_saved_status() == saved_status::delta)) { x_size += f_file->get_size(); if(!f_file->get_storage_size().is_zero() || f_file->get_sparse_file_detection_read()) x_storage_size += f_file->get_storage_size(); else x_storage_size += f_file->get_size(); // in very first archive formats, storage_size was set to zero to // indicate "no compression used" // the only way to have zero as storage_size is either file size is // zero or file is a sparse_file with only zeroed bytes. Sparse file // were not taken into account in that old archive that set storage_size // to zero to indicate the absence of compression } ++it; } updated_sizes = true; } } void cat_directory::recursive_flag_size_to_update() const { if(updated_sizes) { updated_sizes = false; if(parent != nullptr) parent->recursive_flag_size_to_update(); } } void cat_directory::add_children(cat_nomme *r) { cat_directory *d = dynamic_cast(r); const cat_nomme *ancien_nomme; if(r == nullptr) throw SRC_BUG; if(search_children(r->get_name(), ancien_nomme)) // same entry already present { const cat_directory *a_dir = dynamic_cast(ancien_nomme); if(a_dir != nullptr && d != nullptr) // both directories : merging them { a_dir = d; // updates the inode part, does not touch the cat_directory specific part as defined in the cat_directory::operator = deque::iterator xit = d->ordered_fils.begin(); while(xit != d->ordered_fils.end()) { const_cast(a_dir)->add_children(*xit); ++xit; } // need to clear the lists of objects before destroying the cat_directory objects itself // to avoid the destructor destroyed the director children that have been merged to the a_dir cat_directory #ifdef LIBDAR_FAST_DIR d->fils.clear(); #endif d->ordered_fils.clear(); delete r; r = nullptr; d = nullptr; } else // not directories: removing and replacing old entry { if(ancien_nomme == nullptr) throw SRC_BUG; // removing the old object remove(ancien_nomme->get_name()); ancien_nomme = nullptr; // adding the new object #ifdef LIBDAR_FAST_DIR fils[r->get_name()] = r; #endif ordered_fils.push_back(r); } } else // no conflict: adding { #ifdef LIBDAR_FAST_DIR fils[r->get_name()] = r; #endif ordered_fils.push_back(r); } if(d != nullptr) d->parent = this; recursive_flag_size_to_update(); } void cat_directory::reset_read_children() const { it = ordered_fils.begin(); } void cat_directory::end_read() const { // "moi" is necessary to avoid assigning a const_iterator to an iterator cat_directory *moi = const_cast(this); moi->it = moi->ordered_fils.end(); } bool cat_directory::read_children(const cat_nomme *&r) const { if(it != ordered_fils.end()) { if(*it == nullptr) throw SRC_BUG; r = *it; ++it; return true; } else return false; } void cat_directory::erase_ordered_fils(deque::const_iterator debut, deque::const_iterator fin) { for(deque::const_iterator ut = debut; ut != fin; ++ut) if(*ut != nullptr) delete *ut; ordered_fils.erase(debut, fin); } void cat_directory::tail_to_read_children() { #ifdef LIBDAR_FAST_DIR map::iterator dest; deque::const_iterator ordered_dest = it; while(ordered_dest != ordered_fils.end()) { try { if(*ordered_dest == nullptr) throw SRC_BUG; dest = fils.find((*ordered_dest)->get_name()); if(dest == fils.end()) throw SRC_BUG; fils.erase(dest); ordered_dest++; } catch(...) { erase_ordered_fils(it, ordered_dest); it = ordered_fils.end(); throw; } } #endif erase_ordered_fils(it, ordered_fils.end()); it = ordered_fils.end(); recursive_flag_size_to_update(); } void cat_directory::remove(const string & name) { // locating old object in ordered_fils deque::iterator ot = ordered_fils.begin(); while(ot != ordered_fils.end() && *ot != nullptr && (*ot)->get_name() != name) ++ot; if(ot == ordered_fils.end()) throw Erange("cat_directory::remove", tools_printf(gettext("Cannot remove nonexistent entry %S from catalogue"), &name)); if(*ot == nullptr) throw SRC_BUG; #ifdef LIBDAR_FAST_DIR // localizing old object in fils map::iterator ut = fils.find(name); if(ut == fils.end()) throw SRC_BUG; // sanity checks if(*ot != ut->second) throw SRC_BUG; // removing reference from fils fils.erase(ut); #endif // recording the address of the object to remove cat_nomme *obj = *ot; // removing its reference from ordered_fils // and having "it" pointing to the entry following the // removed one, if it would have been the next entry to be read if(it == ot) it = ordered_fils.erase(ot); else // not removing the next to read object (the one pointed to by 'it') { (void)ordered_fils.erase(ot); // however if we remove the last item of the deque // we must set 'it' to end() to avoid 'it' becoming // a dangling pointer: if(ordered_fils.empty()) it = ordered_fils.end(); } // destroying the object itself delete obj; recursive_flag_size_to_update(); } void cat_directory::recursively_set_to_unsaved_data_and_FSA() { deque::iterator it = ordered_fils.begin(); cat_directory *n_dir = nullptr; cat_inode *n_ino = nullptr; cat_mirage *n_mir = nullptr; // dropping info for the current cat_directory set_saved_status(saved_status::not_saved); if(ea_get_saved_status() == ea_saved_status::full) ea_set_saved_status(ea_saved_status::partial); if(fsa_get_saved_status() == fsa_saved_status::full) fsa_set_saved_status(fsa_saved_status::partial); // doing the same for each entry found in that cat_directory while(it != ordered_fils.end()) { if(*it == nullptr) throw SRC_BUG; n_dir = dynamic_cast(*it); n_ino = dynamic_cast(*it); n_mir = dynamic_cast(*it); if(n_mir != nullptr) n_ino = n_mir->get_inode(); if(n_dir != nullptr) n_dir->recursively_set_to_unsaved_data_and_FSA(); else // nothing to do for cat_directory the recursive call does the job { if(n_ino != nullptr) { n_ino->set_saved_status(saved_status::not_saved); if(n_ino->ea_get_saved_status() == ea_saved_status::full) n_ino->ea_set_saved_status(ea_saved_status::partial); if(n_ino->fsa_get_saved_status() == fsa_saved_status::full) n_ino->fsa_set_saved_status(fsa_saved_status::partial); } } ++it; } } void cat_directory::change_location(const smart_pointer & pdesc) { deque::iterator tmp_it = ordered_fils.begin(); cat_nomme::change_location(pdesc); while(tmp_it != ordered_fils.end()) { if(*tmp_it == nullptr) throw SRC_BUG; (*tmp_it)->change_location(pdesc); ++tmp_it; } } void cat_directory::init() noexcept { parent = nullptr; #ifdef LIBDAR_FAST_DIR fils.clear(); #endif ordered_fils.clear(); it = ordered_fils.begin(); updated_sizes = false; } void cat_directory::clear() { #ifdef LIBDAR_FAST_DIR fils.clear(); #endif erase_ordered_fils(ordered_fils.begin(), ordered_fils.end()); ordered_fils.clear(); it = ordered_fils.begin(); recursive_flag_size_to_update(); } bool cat_directory::search_children(const string &name, const cat_nomme * & ptr) const { #ifdef LIBDAR_FAST_DIR map::const_iterator ut = fils.find(name); if(ut != fils.end()) { if(ut->second == nullptr) throw SRC_BUG; ptr = ut->second; if(ptr == nullptr) throw SRC_BUG; } else ptr = nullptr; #else deque::const_iterator ot = ordered_fils.begin(); while(ot != ordered_fils.end() && *ot != nullptr && (*ot)->get_name() != name) ++ot; if(ot != ordered_fils.end()) { ptr = *ot; if(ptr == nullptr) throw SRC_BUG; } else ptr = nullptr; #endif return ptr != nullptr; } void cat_directory::recursive_has_changed_update() const { deque::const_iterator it = ordered_fils.begin(); recursive_has_changed = false; while(it != ordered_fils.end()) { const cat_directory *d = dynamic_cast(*it); const cat_inode *ino = dynamic_cast(*it); if(d != nullptr) { d->recursive_has_changed_update(); recursive_has_changed |= d->get_recursive_has_changed(); } if(ino != nullptr && !recursive_has_changed) recursive_has_changed |= ino->get_saved_status() != saved_status::not_saved || ino->ea_get_saved_status() == ea_saved_status::full || ino->ea_get_saved_status() == ea_saved_status::removed; ++it; } } infinint cat_directory::get_tree_size() const { infinint ret = ordered_fils.size(); const cat_directory *fils_dir = nullptr; deque::const_iterator ot = ordered_fils.begin(); while(ot != ordered_fils.end()) { if(*ot == nullptr) throw SRC_BUG; fils_dir = dynamic_cast(*ot); if(fils_dir != nullptr) ret += fils_dir->get_tree_size(); ++ot; } return ret; } infinint cat_directory::get_tree_ea_num() const { infinint ret = 0; deque::const_iterator it = ordered_fils.begin(); while(it != ordered_fils.end()) { const cat_directory *fils_dir = dynamic_cast(*it); const cat_inode *fils_ino = dynamic_cast(*it); const cat_mirage *fils_mir = dynamic_cast(*it); if(fils_mir != nullptr) fils_ino = fils_mir->get_inode(); if(fils_ino != nullptr) if(fils_ino->ea_get_saved_status() != ea_saved_status::none && fils_ino->ea_get_saved_status() != ea_saved_status::removed) ++ret; if(fils_dir != nullptr) ret += fils_dir->get_tree_ea_num(); ++it; } return ret; } infinint cat_directory::get_tree_mirage_num() const { infinint ret = 0; deque::const_iterator it = ordered_fils.begin(); while(it != ordered_fils.end()) { const cat_directory *fils_dir = dynamic_cast(*it); const cat_mirage *fils_mir = dynamic_cast(*it); if(fils_mir != nullptr) ++ret; if(fils_dir != nullptr) ret += fils_dir->get_tree_mirage_num(); ++it; } return ret; } void cat_directory::get_etiquettes_found_in_tree(map & already_found) const { deque::const_iterator it = ordered_fils.begin(); while(it != ordered_fils.end()) { const cat_mirage *fils_mir = dynamic_cast(*it); const cat_directory *fils_dir = dynamic_cast(*it); if(fils_mir != nullptr) { map::iterator tiq = already_found.find(fils_mir->get_etiquette()); if(tiq == already_found.end()) already_found[fils_mir->get_etiquette()] = 1; else already_found[fils_mir->get_etiquette()] = tiq->second + 1; // due to st::map implementation, it is not recommanded to modify an entry directly // using a "pair" structure (the one that holds .first and .second fields) } if(fils_dir != nullptr) fils_dir->get_etiquettes_found_in_tree(already_found); ++it; } } void cat_directory::remove_all_mirages_and_reduce_dirs() { deque::iterator curs = ordered_fils.begin(); while(curs != ordered_fils.end()) { if(*curs == nullptr) throw SRC_BUG; cat_directory *d = dynamic_cast(*curs); cat_mirage *m = dynamic_cast(*curs); cat_nomme *n = dynamic_cast(*curs); // sanity check if((m != nullptr && n == nullptr) || (d != nullptr && n == nullptr)) throw SRC_BUG; // recursive call if(d != nullptr) d->remove_all_mirages_and_reduce_dirs(); if(m != nullptr || (d != nullptr && d->is_empty())) { #ifdef LIBDAR_FAST_DIR map::iterator monfils = fils.find(n->get_name()); if(monfils == fils.end()) throw SRC_BUG; if(monfils->second != *curs) throw SRC_BUG; fils.erase(monfils); #endif curs = ordered_fils.erase(curs); // curs now points to the next item delete n; } else ++curs; } recursive_flag_size_to_update(); } void cat_directory::set_all_mirage_s_inode_wrote_field_to(bool val) const { deque ::const_iterator curs = ordered_fils.begin(); const cat_mirage *mir = nullptr; const cat_directory *dir = nullptr; while(curs != ordered_fils.end()) { mir = dynamic_cast(*curs); dir = dynamic_cast(*curs); if(mir != nullptr) mir->set_inode_wrote(val); if(dir != nullptr) dir->set_all_mirage_s_inode_wrote_field_to(val); ++curs; } } void cat_directory::set_all_mirage_s_inode_dumped_field_to(bool val) const { deque::const_iterator curs = ordered_fils.begin(); while(curs != ordered_fils.end()) { if(*curs == nullptr) throw SRC_BUG; const cat_directory *d = dynamic_cast(*curs); const cat_mirage *m = dynamic_cast(*curs); // recursive call if(d != nullptr) d->set_all_mirage_s_inode_dumped_field_to(val); if(m != nullptr) m->set_inode_dumped(val); ++curs; } } } // end of namespace dar-2.7.15/src/libdar/filesystem_restore.cpp0000644000175000017500000011076614636066467016011 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif #if HAVE_TIME_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_SYS_UN_H #include #endif #if HAVE_UNISTD_H #include #endif #if STDC_HEADERS #include #endif #ifdef LIBDAR_NODUMP_FEATURE #if HAVE_SYS_IOCTL_H #include #endif #if LIBDAR_NODUMP_FEATURE == NODUMP_LINUX #include #else #if LIBDAR_NODUMP_FEATURE == NODUMP_EXT2FS #include #else #error "unknown location of ext2_fs.h include file" #endif #endif #endif #if MAJOR_IN_MKDEV #include #if !defined(makedev) && defined(mkdev) #define makedev(a,b) mkdev((a),(b)) #endif #else #if MAJOR_IN_SYSMACROS #include #endif #endif } // end extern "C" #include #include #include "filesystem_restore.hpp" #include "tools.hpp" #include "filesystem_tools.hpp" #include "erreurs.hpp" #include "user_interaction.hpp" #include "cat_all_entrees.hpp" #include "ea_filesystem.hpp" #include "cygwin_adapt.hpp" #include "fichier_local.hpp" #include "generic_rsync.hpp" #include "null_file.hpp" #include "cat_signature.hpp" #include "compile_time_features.hpp" #include "op_tools.hpp" using namespace std; namespace libdar { filesystem_restore::filesystem_restore(const std::shared_ptr & dialog, const path &root, bool x_warn_overwrite, bool x_info_details, const mask & x_ea_mask, comparison_fields x_what_to_check, bool x_warn_remove_no_match, bool x_empty, const crit_action *x_overwrite, bool x_only_overwrite, const fsa_scope & scope): filesystem_hard_link_write(dialog), filesystem_hard_link_read(dialog, compile_time::furtive_read(), scope) { fs_root = nullptr; ea_mask = nullptr; current_dir = nullptr; overwrite = nullptr; try { fs_root = filesystem_tools_get_root_with_symlink(*dialog, root, x_info_details); if(fs_root == nullptr) throw Ememory("filesystem_write::filesystem_write"); ea_mask = x_ea_mask.clone(); if(ea_mask == nullptr) throw Ememory("filesystem_restore::filesystem_restore"); if(x_overwrite == nullptr) throw SRC_BUG; overwrite = x_overwrite->clone(); if(overwrite == nullptr) throw Ememory("filesystem_restore::filesystem_restore"); } catch(...) { detruire(); throw; } warn_overwrite = x_warn_overwrite; info_details = x_info_details; what_to_check = x_what_to_check; warn_remove_no_match = x_warn_remove_no_match; empty = x_empty; only_overwrite = x_only_overwrite; reset_write(); zeroing_negative_dates_without_asking(); // when reading existing inode to evaluate overwriting action } void filesystem_restore::reset_write() { filesystem_hard_link_write::corres_reset(); filesystem_hard_link_read::corres_reset(); stack_dir.clear(); if(current_dir != nullptr) delete current_dir; current_dir = new (nothrow) path(*fs_root); if(current_dir == nullptr) throw Ememory("filesystem_write::reset_write"); ignore_over_restricts = false; } void filesystem_restore::write(const cat_entree *x, action_done_for_data & data_restored, bool & ea_restored, bool & data_created, bool & hard_link, bool & fsa_restored) { const cat_eod *x_eod = dynamic_cast(x); const cat_nomme *x_nom = dynamic_cast(x); const cat_directory *x_dir = dynamic_cast(x); const cat_detruit *x_det = dynamic_cast(x); const cat_inode *x_ino = dynamic_cast(x); const cat_mirage *x_mir = dynamic_cast(x); const cat_file *x_fil = dynamic_cast(x); data_restored = done_no_change_no_data; ea_restored = false; data_created = false; fsa_restored = false; hard_link = x_mir != nullptr && known_etiquette(x_mir->get_etiquette()); if(x_mir != nullptr) { x_ino = x_mir->get_inode(); if(x_ino == nullptr) throw SRC_BUG; x_fil = dynamic_cast(x_ino); } if(x_eod != nullptr) { string tmp; current_dir->pop(tmp); if(!stack_dir.empty()) { try { if(!empty && stack_dir.back().get_restore_date()) { string chem = (current_dir->append(stack_dir.back().get_name())).display(); filesystem_tools_make_date(stack_dir.back(), chem, what_to_check, get_fsa_scope()); filesystem_tools_make_owner_perm(get_ui(), stack_dir.back(), chem, what_to_check, get_fsa_scope()); } } catch(...) { stack_dir.pop_back(); throw; } stack_dir.pop_back(); } else throw SRC_BUG; return; } if(x_nom == nullptr) throw SRC_BUG; // neither "cat_nomme" nor "cat_eod" else // cat_nomme { bool has_data_saved = (x_ino != nullptr && x_ino->get_saved_status() == saved_status::saved) || x_det != nullptr; bool has_patch = x_ino != nullptr && x_ino->get_saved_status() == saved_status::delta; bool has_just_inode = x_ino != nullptr && x_ino->get_saved_status() == saved_status::inode_only; bool has_ea_saved = x_ino != nullptr && (x_ino->ea_get_saved_status() == ea_saved_status::full || x_ino->ea_get_saved_status() == ea_saved_status::removed); bool has_fsa_saved = x_ino != nullptr && x_ino->fsa_get_saved_status() == fsa_saved_status::full; path spot = current_dir->append(x_nom->get_name()); string spot_display = spot.display(); cat_nomme *exists = nullptr; if(ignore_over_restricts) // only used in sequential_read when a file has been saved several times due // to its contents being modified at backup time while dar was reading it. // here when ignore_over_restricts is true it is asked to remove the previously restored copy of // that file because a better copy has been found in the archive. { ignore_over_restricts = false; // just one shot state ; exists == nullptr : we are ignoring existing entry filesystem_tools_supprime(get_ui(), spot_display); if(x_det != nullptr) { data_restored = done_data_removed; data_created = false; hard_link = false; ea_restored = false; fsa_restored = false; if(!stack_dir.empty()) stack_dir.back().set_restore_date(true); return; } } else exists = make_read_entree(*current_dir, x_nom->get_name(), false, *ea_mask); if(has_patch) { if(exists == nullptr) throw Erange("filesystem_restore::write", string(gettext("Cannot restore a delta binary patch without a file to patch on filesystem"))); if(x_fil == nullptr) throw SRC_BUG; } if(has_just_inode) { if(exists == nullptr) throw Erange("filesystem_restore::write", string(gettext("Cannot restore a inode metadata only without an existing file on filesystem"))); } try { cat_inode *exists_ino = dynamic_cast(exists); cat_directory *exists_dir = dynamic_cast(exists); cat_file *exists_file = dynamic_cast(exists); if(exists_ino == nullptr && exists != nullptr) throw SRC_BUG; // an object from filesystem should always be an cat_inode !?! if(exists == nullptr) { // no conflict: there is not an already existing file present in filesystem if(x_det != nullptr) throw Erange("filesystem_restore::write", tools_printf(gettext("Cannot remove non-existent file from filesystem: %S"),& spot_display)); if((has_data_saved || hard_link || x_dir != nullptr) && !only_overwrite) { if(info_details) get_ui().message(string(gettext("Restoring file's data: ")) + spot_display); // 1 - restoring data if(!empty) make_file(x_nom, *current_dir); data_created = true; data_restored = done_data_restored; // recording that we must set back the mtime of the parent directory if(!stack_dir.empty()) stack_dir.back().set_restore_date(true); // we must try to restore EA or FSA only if data could be restored // as in the current situation no file existed before // 2 - restoring EA if(has_ea_saved) { if(info_details) get_ui().message(string(gettext("Restoring file's EA: ")) + spot_display); if(!empty) { const ea_attributs *ea = x_ino->get_ea(); try { ea_restored = raw_set_ea(x_nom, *ea, spot_display, *ea_mask); } catch(Erange & e) { get_ui().message(tools_printf(gettext("Restoration of EA for %S aborted: "), &spot_display) + e.get_message()); } } else ea_restored = true; } // 3 - restoring FSA (including Mac OSX birth date if present) if(has_fsa_saved) { if(info_details) get_ui().message(string(gettext("Restoring file's FSA: ")) + spot_display); if(!empty) { const filesystem_specific_attribute_list * fsa = x_ino->get_fsa(); if(fsa == nullptr) throw SRC_BUG; try { // we must not yet restore linux::immutable flag in order // to restore the permissions and dates that must been // restored first: fsa_restored = fsa->set_fsa_to_filesystem_for(spot_display, get_fsa_scope(), get_ui(), false); } catch(Erange & e) { get_ui().message(tools_printf(gettext("Restoration of FSA for %S aborted: "), &spot_display) + e.get_message()); } } } // now that FSA has been read (if sequential mode is used) // we can restore dates in particular creation date from HFS+ FSA if present // but this is useless for directory as we may restore files in them // dates of directories will be restored when the corresponding EOD will // be met if(!empty && x_dir == nullptr && x_ino != nullptr) { // 4 - restoring dates filesystem_tools_make_date(*x_ino, spot_display, what_to_check, get_fsa_scope()); // 5 - restoring permission and ownership // first system may have removed linux capabilities (part of EA) // due to change of ownership, // second, if dar is not run as root, we may fail any further operation on // this file so we must not report failure if subsenquent action fails filesystem_tools_make_owner_perm(get_ui(), *x_ino, spot_display, what_to_check, get_fsa_scope()); } // 6 - trying re-setting EA after ownership restoration to set back linux capabilities if(has_ea_saved && !empty) { const ea_attributs *ea = x_ino->get_ea(); try { raw_set_ea(x_nom, *ea, spot_display, *ea_mask); } catch(Erange & e) { // error probably due to permission context, // as raw_set_ea() has been called earlier // and either no error met or same error met } } // 7 - setting the linux immutable flag if present if(has_fsa_saved) { const filesystem_specific_attribute_list * fsa = x_ino->get_fsa(); if(fsa == nullptr) throw SRC_BUG; try { if(info_details && fsa->has_linux_immutable_set()) get_ui().message(string(gettext("Restoring linux immutable FSA for ")) + spot_display); fsa_restored = fsa->set_fsa_to_filesystem_for(spot_display, get_fsa_scope(), get_ui(), true); } catch(Erange & e) { get_ui().message(tools_printf(gettext("Restoration of linux immutable FSA for %S aborted: "), &spot_display) + e.get_message()); } } } else // no existing inode but no data to restore { data_restored = done_no_change_no_data; data_created = false; hard_link = false; ea_restored = false; fsa_restored = false; } } else // exists != nullptr { // conflict: an entry of that name is already present in filesystem over_action_data act_data = data_undefined; over_action_ea act_ea = EA_undefined; // 1 - restoring data overwrite->get_action(*exists, *x_nom, act_data, act_ea); if(x_ino == nullptr) if(x_det == nullptr) throw SRC_BUG; else { action_over_remove(exists_ino, x_det, spot_display, act_data); data_restored = done_data_removed; data_created = false; hard_link = false; ea_restored = false; fsa_restored = false; // recording that we must set back the mtime of the parent directory if(!stack_dir.empty()) stack_dir.back().set_restore_date(true); } else // a normal inode (or hard linked one) is to be restored { if(has_data_saved || has_just_inode) { action_over_data(exists_ino, x_nom, spot_display, act_data, data_restored); } else if(has_patch) { if(exists_file != nullptr) { // we do not take care of overwriting data here as it is the // expected behavior for delta binary patching if(info_details) get_ui().message(string(gettext("Restoring file's data using a delta patching: ")) + spot_display); filesystem_tools_make_delta_patch(get_pointer(), *exists_file, spot_display, *x_fil, *current_dir); data_restored = done_data_restored; } else { // cannot overwrite the existing inode as we do not have the base data to use for patching get_ui().message(tools_printf(gettext("Cannot restore delta diff for %S as exsiting inode is not a plain file"), &spot_display)); } } else // no data saved in the object to restore { data_restored = done_no_change_no_data; if(x_mir != nullptr) write_hard_linked_target_if_not_set(x_mir, spot_display); } if(data_restored != done_data_restored) hard_link = false; // here we can restore EA even if no data has been restored // it will modify EA of the existing file if(act_data != data_remove) { // 2 - restoring EA if(has_ea_saved) { try { ea_restored = action_over_ea(exists_ino, x_nom, spot_display, act_ea); } catch(Erange & e) { get_ui().message(tools_printf(gettext("Restoration of EA for %S aborted: "), &spot_display) + e.get_message()); } } // 3 - restoring FSA if(has_fsa_saved) { try { fsa_restored = action_over_fsa(exists_ino, x_nom, spot_display, act_ea); } catch(Erange & e) { get_ui().message(tools_printf(gettext("Restoration of FSA for %S aborted: "), &spot_display) + e.get_message()); } } } // if we can restore metadata on something (existing inode) if(has_fsa_saved || has_ea_saved || has_patch || has_data_saved || has_just_inode) { if(data_restored == done_data_restored) // setting the date, perimssions and ownership to value found in the archive // this can only be done once the EA and FSA have been restored (mtime) // and data has been restore (mtime, atime) { if(exists_dir == nullptr) { // 4 - restoring dates (birthtime has been restored with EA) filesystem_tools_make_date(*x_ino, spot_display, what_to_check, get_fsa_scope()); // 5 - restoring owership and permission with two consequences : // - system erased linux capabilites (EA) // - may not be able to restore further and must not propagate failures // for subnsequent steps filesystem_tools_make_owner_perm(get_ui(), *x_ino, spot_display, what_to_check, get_fsa_scope()); } // directory will get their metadata when the correspondant EOD // will be met, meaning all subfile and directory could be treated // (successfully or not, but no more change is expected in that dir) } else // set back the mtime to value found in filesystem before restoration filesystem_tools_make_date(*exists_ino, spot_display, what_to_check, get_fsa_scope()); } // 6 - restoring linux capabilities if possible if(ea_restored) { // if linux capabilities were restored, changing ownership let them // been removed by the system. And doing restoration of EA after ownership // may avoid being able to restore EA due to lack of privilege if // libdar is not ran as root try { (void)action_over_ea(exists_ino, x_nom, spot_display, act_ea); } catch(Erange & e) { // ignoring any error here // we already restored EA // previously } } // 7 - restoring linux immutable FSA if present if(fsa_restored) { if(!empty) { const cat_mirage *tba_mir = dynamic_cast(x_nom); const cat_inode *tba_ino = dynamic_cast(x_nom); if(tba_mir != nullptr) tba_ino = tba_mir->get_inode(); if(tba_ino == nullptr) throw SRC_BUG; if(tba_ino->fsa_get_saved_status() != fsa_saved_status::full) throw SRC_BUG; const filesystem_specific_attribute_list * fsa = tba_ino->get_fsa(); if(fsa == nullptr) throw SRC_BUG; if(info_details && fsa->has_linux_immutable_set()) get_ui().message(string(gettext("Restoring linux immutable FSA for ")) + spot_display); fsa->set_fsa_to_filesystem_for(spot_display, get_fsa_scope(), get_ui(), true); } } if(act_data == data_remove || data_restored == done_data_restored) { // recording that we must set back the mtime of the parent directory if(!stack_dir.empty()) stack_dir.back().set_restore_date(true); } } } if(x_dir != nullptr && (exists == nullptr || exists_dir != nullptr || data_restored == done_data_restored)) { *current_dir += x_dir->get_name(); stack_dir.push_back(stack_dir_t(*x_dir, data_restored == done_data_restored)); } } catch(...) { if(exists != nullptr) delete exists; throw; } if(exists != nullptr) delete exists; } } void filesystem_restore::action_over_remove(const cat_inode *in_place, const cat_detruit *to_be_added, const string & spot, over_action_data action) { if(in_place == nullptr || to_be_added == nullptr) throw SRC_BUG; if(action == data_ask) action = op_tools_crit_ask_user_for_data_action(get_ui(), spot, in_place, to_be_added); switch(action) { case data_preserve: case data_preserve_mark_already_saved: // nothing to do break; case data_overwrite: case data_overwrite_mark_already_saved: case data_remove: if(warn_overwrite) get_ui().pause(tools_printf(gettext("%S is about to be removed from filesystem, continue?"), &spot)); if(cat_signature::compatible_signature(in_place->signature(), to_be_added->get_signature())) { if(info_details) get_ui().printf(gettext("Removing file (reason is file recorded as removed in archive): %S"), &spot); if(!empty) filesystem_tools_supprime(get_ui(), spot); } else { if(warn_remove_no_match) // warning even if just warn_overwrite is not set get_ui().pause(tools_printf(gettext("%S must be removed, but does not match expected type, remove it anyway ?"), &spot)); if(info_details) get_ui().printf(gettext("Removing file (reason is file recorded as removed in archive): %S"), &spot); if(!empty) filesystem_tools_supprime(get_ui(), spot); } break; case data_undefined: throw Erange("filesystem_restore::action_over_detruit", tools_printf(gettext("%S: Overwriting policy (Data) is undefined for that file, do not know whether removal is allowed or not!"), &spot)); case data_ask: throw SRC_BUG; default: throw SRC_BUG; } } void filesystem_restore::action_over_data(const cat_inode *in_place, const cat_nomme *to_be_added, const string & spot, over_action_data action, action_done_for_data & data_done) { const cat_mirage *tba_mir = dynamic_cast(to_be_added); const cat_inode *tba_ino = tba_mir == nullptr ? dynamic_cast(to_be_added) : tba_mir->get_inode(); const cat_directory *tba_dir = dynamic_cast(to_be_added); const cat_detruit *tba_det = dynamic_cast(to_be_added); const cat_lien *in_place_symlink = dynamic_cast(in_place); if(tba_ino == nullptr) throw SRC_BUG; if(in_place == nullptr) throw SRC_BUG; if(tba_det != nullptr) throw SRC_BUG; // must be either a cat_mirage or an inode, not any other cat_nomme object if(action == data_ask) action = op_tools_crit_ask_user_for_data_action(get_ui(), spot, in_place, to_be_added); switch(action) { case data_preserve: case data_preserve_mark_already_saved: if(tba_dir != nullptr && !tba_ino->same_as(*in_place)) throw Erange("filesystem_write::write", tools_printf(gettext("Directory %S cannot be restored: overwriting not allowed and a non-directory inode of that name already exists, all files in that directory will be skipped for restoration:"), &spot)); data_done = done_no_change_policy; break; case data_overwrite: case data_overwrite_mark_already_saved: if(warn_overwrite) { try { get_ui().pause(tools_printf(gettext("%S is about to be overwritten, OK?"), &spot)); } catch(Euser_abort & e) { if(tba_dir != nullptr && tba_ino->same_as(*in_place)) { data_done = done_no_change_policy; return; // if we throw exception here, we will not recurse in this directory, while we could as a directory exists on filesystem } else throw; // throwing the exception here, implies that no EA will be tried to be restored for that file } } if(info_details) get_ui().message(string(gettext("Restoring file's data: ")) + spot); if((tba_dir != nullptr || tba_ino->get_saved_status() == saved_status::inode_only) && tba_ino->same_as(*in_place)) { if(!empty) filesystem_tools_widen_perm(get_ui(), *tba_ino, spot, what_to_check); data_done = done_data_restored; } else // not both in_place and to_be_added are directories, or we can only restore the inode metadata and existing file is of different type { ea_attributs *ea = nullptr; // saving original EA of existing inode filesystem_specific_attribute_list fsa; // saving original FSA of existing inode bool got_ea = true; bool got_fsa = true; if(tba_ino->get_saved_status() == saved_status::inode_only) throw Erange("filesystem_restore::write", string(gettext("Existing file is of a different nature, cannot only restore inode metadata"))); try { // reading EA present on filesystem try { ea = ea_filesystem_read_ea(spot, bool_mask(true)); } catch(Ethread_cancel & e) { throw; } catch(Egeneric & ex) { got_ea = false; get_ui().message(tools_printf(gettext("Existing EA for %S could not be read and preserved: "), &spot) + ex.get_message()); } // reading FSA present on filesystem try { fsa.get_fsa_from_filesystem_for(get_ui(), spot, all_fsa_families(), in_place_symlink != nullptr, get_ask_before_zeroing_neg_dates()); } catch(Ethread_cancel & e) { throw; } catch(Egeneric & ex) { got_fsa = false; get_ui().message(tools_printf(gettext("Existing FSA for %S could not be read and preserved: "), &spot) + ex.get_message()); } // removing current entry and creating the new entry in place if(!empty) { if(filesystem_tools_has_immutable(*in_place) && filesystem_tools_has_immutable(*tba_ino) && in_place->same_as(*tba_ino)) { // considering this situation is the restoration of a differential backup of the same file, // we must first remove the immutable flag in order to restore the new inode data if(info_details) get_ui().printf(gettext("Removing existing immutable flag in order to restore data for %S"), &spot); filesystem_tools_set_immutable(spot, false, get_ui()); } filesystem_tools_supprime(get_ui(), spot); // this destroyes EA, (removes inode, or hard link to inode) make_file(to_be_added, *current_dir); data_done = done_data_restored; } // restoring EA that were present on filesystem try // if possible and available restoring original EA { if(got_ea && !empty) if(ea != nullptr) // if ea is nullptr no EA is present in the original file, thus nothing has to be restored (void)ea_filesystem_write_ea(spot, *ea, bool_mask(true)); // we don't care about the return value, here, errors are returned through exceptions // the returned value is informative only and does not determine any subsequent actions } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { if(ea != nullptr && !ea->size().is_zero()) get_ui().message(tools_printf(gettext("Existing EA for %S could not be preserved : "), &spot) + e.get_message()); } // restoring FSA that were present on filesystem try // if possible and available restoring original FSA (except the possible immutable flag) { if(got_fsa && !empty) fsa.set_fsa_to_filesystem_for(spot, all_fsa_families(), get_ui(), false); } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { if(ea != nullptr && !ea->size().is_zero()) get_ui().message(tools_printf(gettext("Existing FSA for %S could not be preserved : "), &spot) + e.get_message()); } } catch(...) { if(ea != nullptr) delete ea; throw; } if(ea != nullptr) delete ea; } break; case data_remove: if(warn_overwrite) get_ui().pause(tools_printf(gettext("%S is about to be deleted (required by overwriting policy), do you agree?"), &spot)); if(info_details) get_ui().printf(gettext("Removing file (reason is overwriting policy): %S"), &spot); if(!empty) filesystem_tools_supprime(get_ui(), spot); data_done = done_data_removed; break; case data_undefined: throw Erange("filesystem_restore::action_over_detruit", tools_printf(gettext("%S: Overwriting policy (Data) is undefined for that file, do not know whether overwriting is allowed or not!"), &spot)); case data_ask: throw SRC_BUG; default: throw SRC_BUG; } } bool filesystem_restore::action_over_ea(const cat_inode *in_place, const cat_nomme *to_be_added, const string & spot, over_action_ea action) { bool ret = false; const cat_inode *tba_ino = dynamic_cast(to_be_added); const cat_mirage *tba_mir = dynamic_cast(to_be_added); bool real_overwrite = true; if(tba_mir != nullptr) tba_ino = tba_mir->get_inode(); if(tba_ino == nullptr) throw SRC_BUG; if(in_place == nullptr || to_be_added == nullptr) throw SRC_BUG; if(action == EA_ask) action = op_tools_crit_ask_user_for_EA_action(get_ui(), spot, in_place, to_be_added); // modifying the EA action when the in place inode has not EA #ifdef EA_SUPPORT // but only if EA could be read, which is not the case when EA_SUPPORT is not // activated at compilation time if(in_place->ea_get_saved_status() != ea_saved_status::full) // no EA in filesystem { if(action == EA_merge_preserve || action == EA_merge_overwrite) { action = EA_overwrite; // merging when in_place has no EA is equivalent to overwriting real_overwrite = false; } } #endif if(tba_ino->ea_get_saved_status() == ea_saved_status::removed) // EA have been removed since archive of reference { if(action == EA_merge_preserve || action == EA_merge_overwrite) action = EA_clear; // we must remove EA instead of merging } switch(action) { case EA_preserve: case EA_preserve_mark_already_saved: // nothing to do ret = false; break; case EA_overwrite: case EA_overwrite_mark_already_saved: if(tba_ino->ea_get_saved_status() != ea_saved_status::full && tba_ino->ea_get_saved_status() != ea_saved_status::removed) throw SRC_BUG; if(warn_overwrite && real_overwrite) { try { get_ui().pause(tools_printf(gettext("EA for %S are about to be overwritten, OK?"), &spot)); } catch(Euser_abort & e) { const cat_directory *tba_dir = dynamic_cast(to_be_added); if(tba_dir != nullptr && tba_ino->same_as(*in_place)) return false; else throw; } } if(!empty && !raw_clear_ea_set(to_be_added, spot)) { if(info_details) get_ui().printf(gettext("EA for %S have not been overwritten because this file is a hard link pointing to an already restored inode"), &spot); ret = false; } else // successfully cleared EA { if(info_details) get_ui().message(string(gettext("Restoring file's EA: ")) + spot); const ea_attributs *tba_ea = tba_ino->get_ea(); if(!empty) ret = raw_set_ea(to_be_added, *tba_ea, spot, *ea_mask); else ret = true; } break; case EA_clear: if(warn_overwrite) { try { get_ui().pause(tools_printf(gettext("EA for %S are about to be removed, OK?"), &spot)); } catch(Euser_abort & e) { return false; } } if(!empty && !raw_clear_ea_set(to_be_added, spot)) { if(info_details) get_ui().printf(gettext("EA for %S have not been cleared as requested by the overwriting policy because this file is a hard link pointing to an already restored inode"), &spot); ret = false; } else { if(info_details) get_ui().message(string(gettext("Clearing file's EA (requested by overwriting policy): ")) + spot); ret = true; } break; case EA_merge_preserve: case EA_merge_overwrite: #ifdef EA_SUPPORT if(in_place->ea_get_saved_status() != ea_saved_status::full) throw SRC_BUG; // should have been redirected to EA_overwrite ! #endif if(warn_overwrite) { try { get_ui().pause(tools_printf(gettext("EA for %S are about to be merged, OK?"), &spot)); } catch(Euser_abort & e) { return false; } } if(tba_ino->ea_get_saved_status() == ea_saved_status::full) // Note, that ea_saved_status::removed is the other valid value { const ea_attributs *tba_ea = tba_ino->get_ea(); #ifdef EA_SUPPORT const ea_attributs *ip_ea = in_place->get_ea(); #else const ea_attributs faked_ip_ea; const ea_attributs *ip_ea = & faked_ip_ea; #endif ea_attributs result; if(action == EA_merge_preserve) result = *tba_ea + *ip_ea; else // action == EA_merge_overwrite result = *ip_ea + *tba_ea; // the + operator on ea_attributs is not reflexive !!! if(!empty) ret = raw_set_ea(to_be_added, result, spot, *ea_mask); else ret = true; } break; case EA_undefined: throw Erange("filesystem_restore::action_over_detruit", tools_printf(gettext("%S: Overwriting policy (EA) is undefined for that file, do not know whether overwriting is allowed or not!"), &spot)); case EA_ask: throw SRC_BUG; default: throw SRC_BUG; } return ret; } bool filesystem_restore::action_over_fsa(const cat_inode *in_place, const cat_nomme *to_be_added, const string & spot, over_action_ea action) { bool ret = false; const cat_inode *tba_ino = dynamic_cast(to_be_added); const cat_mirage *tba_mir = dynamic_cast(to_be_added); if(tba_mir != nullptr) tba_ino = tba_mir->get_inode(); if(tba_ino == nullptr) throw SRC_BUG; if(in_place == nullptr || to_be_added == nullptr) throw SRC_BUG; if(action == EA_ask) action = op_tools_crit_ask_user_for_FSA_action(get_ui(), spot, in_place, to_be_added); // modifying the FSA action when the in place inode has not FSA if(in_place->fsa_get_saved_status() != fsa_saved_status::full) // no EA in filesystem { if(action == EA_merge_preserve || action == EA_merge_overwrite) action = EA_overwrite; // merging when in_place has no EA is equivalent to overwriting } switch(action) { case EA_preserve: case EA_preserve_mark_already_saved: // nothing to do ret = false; break; case EA_overwrite: case EA_overwrite_mark_already_saved: if(tba_ino->fsa_get_saved_status() != fsa_saved_status::full) throw SRC_BUG; if(warn_overwrite) { try { get_ui().pause(tools_printf(gettext("FSA for %S are about to be overwritten, OK?"), &spot)); } catch(Euser_abort & e) { const cat_directory *tba_dir = dynamic_cast(to_be_added); if(tba_dir != nullptr && tba_ino->same_as(*in_place)) return false; else throw; } } if(tba_mir != nullptr && known_etiquette(tba_mir->get_etiquette())) { if(info_details) get_ui().printf(gettext("FSA for %S have not been overwritten because this file is a hard link pointing to an already restored inode"), &spot); ret = false; } else { if(info_details) get_ui().message(string(gettext("Restoring file's FSA: ")) + spot); if(!empty) { const filesystem_specific_attribute_list * fsa = tba_ino->get_fsa(); if(fsa == nullptr) throw SRC_BUG; ret = fsa->set_fsa_to_filesystem_for(spot, get_fsa_scope(), get_ui(), false); } else ret = true; } break; case EA_clear: break; case EA_merge_preserve: case EA_merge_overwrite: if(in_place->fsa_get_saved_status() != fsa_saved_status::full) throw SRC_BUG; // should have been redirected to EA_overwrite ! if(warn_overwrite) { try { get_ui().pause(tools_printf(gettext("FSA for %S are about to be overwritten, OK?"), &spot)); } catch(Euser_abort & e) { return false; } } if(tba_ino->fsa_get_saved_status() == fsa_saved_status::full) { const filesystem_specific_attribute_list *tba_fsa = tba_ino->get_fsa(); const filesystem_specific_attribute_list *ip_fsa = in_place->get_fsa(); filesystem_specific_attribute_list result; if(action == EA_merge_preserve) result = *tba_fsa + *ip_fsa; else // action == EA_merge_overwrite result = *ip_fsa + *tba_fsa; // the + operator on FSA is not reflexive !!! if(!empty) ret = result.set_fsa_to_filesystem_for(spot, get_fsa_scope(), get_ui(), false); else ret = true; } break; case EA_undefined: throw Erange("filesystem_restore::action_over_detruit", tools_printf(gettext("%S: Overwriting policy (FSA) is undefined for that file, do not know whether overwriting is allowed or not!"), &spot)); case EA_ask: throw SRC_BUG; default: throw SRC_BUG; } return ret; } void filesystem_restore::detruire() { if(fs_root != nullptr) { delete fs_root; fs_root = nullptr; } if(current_dir != nullptr) { delete current_dir; current_dir = nullptr; } if(ea_mask != nullptr) { delete ea_mask; ea_mask = nullptr; } if(overwrite != nullptr) { delete overwrite; overwrite = nullptr; } } void filesystem_restore::restore_stack_dir_ownership() { string tmp; while(!stack_dir.empty() && current_dir->pop(tmp)) { string chem = (current_dir->append(stack_dir.back().get_name())).display(); if(!empty) filesystem_tools_make_owner_perm(get_ui(), stack_dir.back(), chem, what_to_check, get_fsa_scope()); stack_dir.pop_back(); } if(stack_dir.size() > 0) throw SRC_BUG; } } // end of namespace dar-2.7.15/src/libdar/database_listing_callback.hpp0000644000175000017500000000741514636066467017174 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file database_listing_callback.hpp /// \brief definition of the user defined callback function used for database listing /// \ingroup API #ifndef DATABASE_LISTING_CALLBACK_HPP #define DATABASE_LISTING_CALLBACK_HPP #include "../my_config.h" #include #include "database_aux.hpp" #include "archive_num.hpp" #include "datetime.hpp" namespace libdar { /// \addtogroup API /// @{ /// called by the database::get_files() routine /// \param[in] tag is passed as is from the callback caller (database::get_files) /// \param[in] filename the name of the new entry /// \param[in] available_data whether this entry has data saved in the requested archive /// \param[in] available_ea whether this entry has ea saved in the requested archive using database_listing_show_files_callback = void (*) (void *context, const std::string & filename, bool available_data, bool available_ea); /// called with the information of presence for an entry in archive number num /// \param[in] tag is passed as is from the callback caller (database::get_version) /// \param[in] num number of the archive the information is taken from /// \param[in] data_presence status of the data for the requested file in that archive "num" /// \param[in] has_data_date when false the following argument (data) is meaningless /// \param[in] data modification date of the archive of the requested file in the archive "num" /// \param[in] ea_presence status of the EA for the requested file in the archive "num" /// \param[in] has_ea_date when false the following argument (ea) is meaningless /// \param[in] ea change date of the EA for the requested file in the archive "num" using database_listing_get_version_callback = void (*) (void *context, archive_num num, db_etat data_presence, bool has_data_date, datetime data, db_etat ea_presence, bool has_ea_date, datetime ea); /// called with teh information of statistics for each archive in turn /// \param[in] context is passed as is from the callback caller (database::show_most_recent_stats) /// \param[in] number the archive number in the database /// \param[in] data_count amount of file which last version is located in this archive /// \param[in] total_data total number of file covered in this database /// \param[in] ea_count amount of EA which last version is located in this archive /// \param[in] total_ea total number of file that have EA covered by this database using database_listing_statistics_callback = void (*) (void *context, U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/i_archive.cpp0000644000175000017500000026640514636067146014010 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // extern "C" #include "infinint.hpp" #include "generic_file.hpp" #include "archive.hpp" #include "sar.hpp" #include "macro_tools.hpp" #include "filtre.hpp" #include "sar.hpp" #include "trivial_sar.hpp" #include "tools.hpp" #include "header.hpp" #include "header_version.hpp" #include "scrambler.hpp" #include "null_file.hpp" #include "crypto.hpp" #include "elastic.hpp" #include "terminateur.hpp" #include "thread_cancellation.hpp" #include "erreurs_ext.hpp" #include "cache.hpp" #include "entrepot.hpp" #include "entrepot_local.hpp" #include "crypto_sym.hpp" #include "cat_all_entrees.hpp" #include "zapette.hpp" #include "path.hpp" #include "defile.hpp" #include "escape.hpp" #include "escape_catalogue.hpp" #include "nls_swap.hpp" #include "i_archive.hpp" #include "capabilities.hpp" #define ARCHIVE_NOT_EXPLOITABLE "Archive of reference given is not exploitable" using namespace std; namespace libdar { /// checks file is not dirty when reading in sequential mode static bool local_check_dirty_seq(escape *ptr); static void check_libgcrypt_hash_bug(user_interaction & dialog, hash_algo hash, const infinint & first_file_size, const infinint & file_size); // opens an already existing archive archive::i_archive::i_archive(const std::shared_ptr & dialog, const path & chem, const string & basename, const string & extension, const archive_options_read & options): mem_ui(dialog) { shared_ptr where = options.get_entrepot(); bool info_details = options.get_info_details(); if(where == nullptr) throw Ememory("archive::i_archive::archive"); cat = nullptr; try { infinint second_terminateur_offset = 0; infinint ref_second_terminateur_offset = 0; header_version ref_ver; pile_descriptor pdesc; list tmp1_signatories; list tmp2_signatories; lax_read_mode = options.get_lax(); sequential_read = options.get_sequential_read(); // updating the archive object's field where->set_location(chem); try { if(info_details) dialog->printf(gettext("Opening archive %s ..."), basename.c_str()); // we open the main archive to get the different layers (level1, scram and level2). macro_tools_open_archive(get_pointer(), where, basename, options.get_slice_min_digits(), extension, options.get_crypto_algo(), options.get_crypto_pass(), options.get_crypto_size(), stack, ver, options.get_input_pipe(), options.get_output_pipe(), options.get_execute(), second_terminateur_offset, options.get_lax(), options.is_external_catalogue_set(), options.get_sequential_read(), options.get_info_details(), gnupg_signed, slices, options.get_multi_threaded_crypto(), options.get_multi_threaded_compress(), options.get_header_only()); if(options.get_header_only()) { ver.display(get_ui()); throw Erange("archive::i_archive::achive", gettext("header only mode asked")); } pdesc = pile_descriptor(&stack); if(options.is_external_catalogue_set()) { pile ref_stack; shared_ptr ref_where = options.get_ref_entrepot(); if(ref_where == nullptr) throw Ememory("archive::i_archive::archive"); if(info_details) dialog->printf(gettext("Opening the archive of reference %s to retreive the isolated catalog ... "), options.get_ref_basename().c_str()); try { ref_where->set_location(options.get_ref_path()); try { slice_layout ignored; if(options.get_ref_basename() == "-") throw Erange("archive::i_archive::archive", gettext("Reading the archive of reference from pipe or standard input is not possible")); if(options.get_ref_basename() == "+") throw Erange("archive::i_archive::archive", gettext("The basename '+' is reserved for special a purpose that has no meaning in this context")); // we open the archive of reference also to get its different layers (ref_stack) macro_tools_open_archive(get_pointer(), ref_where, options.get_ref_basename(), options.get_ref_slice_min_digits(), extension, options.get_ref_crypto_algo(), options.get_ref_crypto_pass(), options.get_ref_crypto_size(), ref_stack, ref_ver, "", "", options.get_ref_execute(), ref_second_terminateur_offset, options.get_lax(), false, // has an external catalogue false, // sequential_read is never used to retreive the isolated catalogue (well, that's possible and easy to add this feature), see later ... options.get_info_details(), tmp1_signatories, ignored, options.get_multi_threaded_crypto(), options.get_multi_threaded_compress(), false); // we do not comparing the signatories of the archive of reference with the current archive // for example the isolated catalogue might be unencrypted and thus not signed } catch(Euser_abort & e) { throw; } catch(Ebug & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { throw Erange("archive::i_archive::archive", string(gettext("Error while opening the archive of reference: ")) + e.get_message()); } } catch(...) { ref_where.reset(); throw; } ref_where.reset(); // fetching the catalogue in the archive of reference, making it point on the main archive layers. ref_ver.set_compression_algo(ver.get_compression_algo()); // set the default encryption to use to the one of the main archive if(info_details) dialog->message(gettext("Loading isolated catalogue in memory...")); cat = macro_tools_get_derivated_catalogue_from(dialog, stack, ref_stack, ref_ver, options.get_info_details(), local_cat_size, ref_second_terminateur_offset, tmp2_signatories, false); // never relaxed checking for external catalogue if(!same_signatories(tmp1_signatories, tmp2_signatories)) dialog->pause(gettext("Archive of reference is not signed properly (no the same signatories for the archive and the internal catalogue), do we continue?")); if(cat == nullptr) throw SRC_BUG; // checking for compatibility of the archive of reference with this archive data_name if(get_layer1_data_name() != get_catalogue_data_name()) throw Erange("archive::i_archive::archive", gettext("The archive and the isolated catalogue do not correspond to the same data, they are thus incompatible between them")); // we drop delta signature as they refer to the isolated catalogue container/archive // to avoid having to fetch them at wrong offset from the original archive we created // this isolated catalogue from. Anyway we do not need them to read an archive, we // only need delta signatures for archive differential backup, in which case we use the // original archive *or* the isolated catalogue *alone* cat->drop_delta_signatures(); } else // no isolated archive to fetch the catalogue from { try { if(!options.get_sequential_read()) { if(info_details) dialog->message(gettext("Loading catalogue into memory...")); cat = macro_tools_get_catalogue_from(dialog, stack, ver, options.get_info_details(), local_cat_size, second_terminateur_offset, tmp1_signatories, options.get_lax()); if(!same_signatories(tmp1_signatories, gnupg_signed)) { string msg = gettext("Archive internal catalogue is not identically signed as the archive itself, this might be the sign the archive has been compromised"); if(lax_read_mode) dialog->pause(msg); else throw Edata(msg); } } else // sequentially reading { if(pdesc.esc != nullptr) // escape layer is present { if(pdesc.esc->skip_to_next_mark(escape::seqt_catalogue, false)) { if(info_details) dialog->message(gettext("No data found in that archive, sequentially reading the catalogue found at the end of the archive...")); pdesc.stack->flush_read_above(pdesc.esc); contextual *layer1 = nullptr; label lab = label_zero; stack.find_first_from_bottom(layer1); if(layer1 != nullptr) lab = layer1->get_data_name(); cat = macro_tools_read_catalogue(dialog, ver, pdesc, 0, // cannot determine cat_size at this stage tmp1_signatories, options.get_lax(), lab, false); // only_detruits if(!same_signatories(tmp1_signatories, gnupg_signed)) { string msg = gettext("Archive internal catalogue is not identically signed as the archive itself, this might be the sign the archive has been compromised"); if(lax_read_mode) dialog->pause(msg); else throw Edata(msg); } } else { if(info_details) dialog->message(gettext("The catalogue will be filled while sequentially reading the archive, preparing the data structure...")); cat = new (nothrow) escape_catalogue(dialog, pdesc, ver, gnupg_signed, options.get_lax()); } if(cat == nullptr) throw Ememory("archive::i_archive::archive"); } else throw SRC_BUG; } } catch(Ebug & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Euser_abort & e) { throw; } catch(Ememory & e) { throw; } catch(...) { if(!options.get_lax()) throw; else // we have tried and failed to read the whole catalogue, now trying to workaround data corruption if possible { if(options.get_sequential_read()) throw; else // legacy extraction of the catalogue (not sequential mode) { dialog->printf(gettext("LAX MODE: The end of the archive is corrupted, cannot get the archive contents (the \"catalogue\")")); dialog->pause(gettext("LAX MODE: Do you want to bypass some sanity checks and try again reading the archive contents (this may take some time, this may also fail)?")); try { label tmp; tmp.clear(); // this way we do not modify the catalogue data name even in lax mode cat = macro_tools_lax_search_catalogue(dialog, stack, ver.get_edition(), ver.get_compression_algo(), options.get_info_details(), false, // even partial tmp); } catch(Erange & e) { dialog->printf(gettext("LAX MODE: Could not find a whole catalogue in the archive. If you have an isolated catalogue, stop here and use it as backup of the internal catalogue, else continue but be advised that all data will not be able to be retrieved...")); dialog->pause(gettext("LAX MODE: Do you want to try finding portions of the original catalogue if some remain (this may take even more time and in any case, it will only permit to recover some files, at most)?")); cat = macro_tools_lax_search_catalogue(dialog, stack, ver.get_edition(), ver.get_compression_algo(), options.get_info_details(), true, // even partial get_layer1_data_name()); } } } } } if(!options.get_ignore_signature_check_failure()) check_gnupg_signed(); exploitable = true; } catch(...) { where.reset(); throw; } where.reset(); } catch(...) { free_mem(); throw; } } // creates a new archive archive::i_archive::i_archive(const std::shared_ptr & dialog, const path & fs_root, const path & sauv_path, const string & filename, const string & extension, const archive_options_create & options, statistics * progressive_report): mem_ui(dialog) { NLS_SWAP_IN; try { cat = nullptr; shared_ptr sauv_path_t = options.get_entrepot(); if(!sauv_path_t) throw Ememory("archive::i_archive::archive"); sauv_path_t->set_user_ownership(options.get_slice_user_ownership()); sauv_path_t->set_group_ownership(options.get_slice_group_ownership()); sauv_path_t->set_location(sauv_path); filesystem_ids same_fs(options.get_same_fs(), fs_root); deque same_fs_inc = options.get_same_fs_include(); deque same_fs_exc = options.get_same_fs_exclude(); if(!same_fs_inc.empty() || !same_fs_exc.empty()) { deque::iterator it = same_fs_inc.begin(); same_fs.clear(); while(it != same_fs_inc.end()) { same_fs.include_fs_at(*it); ++it; } it = same_fs_exc.begin(); while(it != same_fs_exc.end()) { same_fs.exclude_fs_at(*it); ++it; } } try { sequential_read = false; // updating the archive field (void)op_create_in(oper_create, tools_relative2absolute_path(fs_root, tools_getcwd()), sauv_path_t, options.get_reference().get(), options.get_selection(), options.get_subtree(), filename, extension, options.get_allow_over(), options.get_warn_over(), options.get_info_details(), options.get_display_treated(), options.get_display_treated_only_dir(), options.get_display_skipped(), options.get_display_finished(), options.get_pause(), options.get_empty_dir(), options.get_compression(), options.get_compression_level(), options.get_compression_block_size(), options.get_slice_size(), options.get_first_slice_size(), options.get_ea_mask(), options.get_execute(), options.get_crypto_algo(), options.get_crypto_pass(), options.get_crypto_size(), options.get_gnupg_recipients(), options.get_gnupg_signatories(), options.get_compr_mask(), options.get_min_compr_size(), options.get_nodump(), options.get_exclude_by_ea(), options.get_hourshift(), options.get_empty(), options.get_alter_atime(), options.get_furtive_read_mode(), same_fs, options.get_comparison_fields(), options.get_snapshot(), options.get_cache_directory_tagging(), options.get_fixed_date(), options.get_slice_permission(), options.get_repeat_count(), options.get_repeat_byte(), options.get_sequential_marks(), options.get_security_check(), options.get_sparse_file_min_size(), options.get_user_comment(), options.get_hash_algo(), options.get_slice_min_digits(), options.get_backup_hook_file_execute(), options.get_backup_hook_file_mask(), options.get_ignore_unknown_inode_type(), options.get_fsa_scope(), options.get_multi_threaded_crypto(), options.get_multi_threaded_compress(), options.get_delta_signature(), options.get_has_delta_mask_been_set(), options.get_delta_mask(), options.get_delta_sig_min_size(), options.get_delta_diff(), options.get_auto_zeroing_neg_dates(), options.get_ignored_as_symlink(), options.get_modified_data_detection(), options.get_iteration_count(), options.get_kdf_hash(), options.get_sig_block_len(), progressive_report); exploitable = false; stack.terminate(); } catch(...) { sauv_path_t.reset(); throw; } sauv_path_t.reset(); } catch(...) { free_mem(); throw; } } // merge constructor archive::i_archive::i_archive(const std::shared_ptr & dialog, const path & sauv_path, shared_ptr ref_arch1, const string & filename, const string & extension, const archive_options_merge & options, statistics * progressive_report): mem_ui(dialog) { statistics st = false; statistics *st_ptr = progressive_report == nullptr ? &st : progressive_report; catalogue *ref_cat1 = nullptr; catalogue *ref_cat2 = nullptr; shared_ptr ref_arch2 = options.get_auxiliary_ref(); compression algo_kept = compression::none; U_I comp_bs_kept = 0; infinint i_comp_bs_kept; shared_ptr sauv_path_t = options.get_entrepot(); cat = nullptr; try { if(sauv_path_t == nullptr) throw Ememory("archive::i_archive::archive(merge)"); sauv_path_t->set_user_ownership(options.get_slice_user_ownership()); sauv_path_t->set_group_ownership(options.get_slice_group_ownership()); sauv_path_t->set_location(sauv_path); try { exploitable = false; sequential_read = false; // updating the archive field // sanity checks as much as possible to avoid libdar crashing due to bad arguments // useless arguments are not reported. if(options.get_compression_level() > 9 || options.get_compression_level() < 1) throw Elibcall("op_merge", gettext("Compression_level must be between 1 and 9 included")); if(options.get_slice_size().is_zero() && !options.get_first_slice_size().is_zero()) throw Elibcall("op_merge", gettext("\"first_file_size\" cannot be different from zero if \"file_size\" is equal to zero")); if(options.get_crypto_size() < 10 && options.get_crypto_algo() != crypto_algo::none) throw Elibcall("op_merge", gettext("Crypto block size must be greater than 10 bytes")); check_libgcrypt_hash_bug(get_ui(), options.get_hash_algo(), options.get_first_slice_size(), options.get_slice_size()); if(ref_arch1) if(ref_arch1->pimpl->only_contains_an_isolated_catalogue()) // convert all data to unsaved ref_arch1->set_to_unsaved_data_and_FSA(); if(ref_arch2) if(ref_arch2->pimpl->only_contains_an_isolated_catalogue()) ref_arch2->set_to_unsaved_data_and_FSA(); // end of sanity checks sauv_path_t->set_location(sauv_path); tools_avoid_slice_overwriting_regex(get_ui(), *sauv_path_t, filename, extension, options.get_info_details(), options.get_allow_over(), options.get_warn_over(), options.get_empty()); if(ref_arch1 == nullptr) if(!ref_arch2) throw Elibcall("archive::i_archive::archive[merge]", string(gettext("Both reference archive are nullptr, cannot merge archive from nothing"))); else if(ref_arch2->pimpl->cat == nullptr) throw SRC_BUG; // an archive should always have a catalogue available else if(ref_arch2->pimpl->exploitable) ref_cat1 = ref_arch2->pimpl->cat; else throw Elibcall("archive::i_archive::archive[merge]", gettext(ARCHIVE_NOT_EXPLOITABLE)); else if(!ref_arch2) if(ref_arch1->pimpl->cat == nullptr) throw SRC_BUG; // an archive should always have a catalogue available else if(ref_arch1->pimpl->exploitable) ref_cat1 = ref_arch1->pimpl->cat; else throw Elibcall("archive::i_archive::archive[merge]", gettext(ARCHIVE_NOT_EXPLOITABLE)); else // both catalogues available { if(!ref_arch1->pimpl->exploitable || !ref_arch2->pimpl->exploitable) throw Elibcall("archive::i_archive::archive[merge]", gettext(ARCHIVE_NOT_EXPLOITABLE)); if(ref_arch1->pimpl->cat == nullptr) throw SRC_BUG; if(ref_arch2->pimpl->cat == nullptr) throw SRC_BUG; ref_cat1 = ref_arch1->pimpl->cat; ref_cat2 = ref_arch2->pimpl->cat; if((ref_arch1->pimpl->ver.get_compression_algo() != ref_arch2->pimpl->ver.get_compression_algo() || ref_arch1->pimpl->ver.get_compression_block_size() != ref_arch2->pimpl->ver.get_compression_block_size()) && ref_arch1->pimpl->ver.get_compression_algo() != compression::none && ref_arch2->pimpl->ver.get_compression_algo() != compression::none && options.get_keep_compressed()) throw Efeature(gettext("the \"Keep file compressed\" feature is not possible when merging two archives using different compression algorithms (This is for a future version of dar). You can still merge these two archives but without keeping file compressed (thus you will probably like to use compression (-z or -y options) for the resulting archive")); } if(options.get_keep_compressed()) { if(ref_arch1 == nullptr) throw SRC_BUG; algo_kept = ref_arch1->pimpl->ver.get_compression_algo(); i_comp_bs_kept = ref_arch1->pimpl->ver.get_compression_block_size(); if(algo_kept == compression::none && ref_cat2 != nullptr) { if(!ref_arch2) throw SRC_BUG; else { algo_kept = ref_arch2->pimpl->ver.get_compression_algo(); i_comp_bs_kept = ref_arch2->pimpl->ver.get_compression_block_size(); } } comp_bs_kept = 0; i_comp_bs_kept.unstack(comp_bs_kept); if(!i_comp_bs_kept.is_zero()) throw Erange("archive::i_archive::i_archive(merge)", gettext("compression block size used in the archive exceed integer capacity of the current system")); } if(ref_cat1 == nullptr) throw SRC_BUG; if(options.get_delta_signature()) { if(options.get_keep_compressed() && options.get_has_delta_mask_been_set()) throw Elibcall("op_merge", gettext("Cannot calculate delta signature when merging if keep compressed is asked")); if(options.get_sparse_file_min_size().is_zero() && options.get_has_delta_mask_been_set()) dialog->message(gettext("To calculate delta signatures of files saved as sparse files, you need to activate sparse file detection mechanism with merging operation")); } // then we call op_create_in_sub which will call filter_merge operation to build the archive described by the catalogue op_create_in_sub(oper_merge, FAKE_ROOT, sauv_path_t, ref_cat1, ref_cat2, false, // initial_pause options.get_selection(), options.get_subtree(), filename, extension, options.get_allow_over(), options.get_overwriting_rules(), options.get_warn_over(), options.get_info_details(), options.get_display_treated(), options.get_display_treated_only_dir(), options.get_display_skipped(), false, // display_finished options.get_pause(), options.get_empty_dir(), options.get_keep_compressed() ? algo_kept : options.get_compression(), options.get_compression_level(), options.get_keep_compressed() ? comp_bs_kept : options.get_compression_block_size(), options.get_slice_size(), options.get_first_slice_size(), options.get_ea_mask(), options.get_execute(), options.get_crypto_algo(), options.get_crypto_pass(), options.get_crypto_size(), options.get_gnupg_recipients(), options.get_gnupg_signatories(), options.get_compr_mask(), options.get_min_compr_size(), false, // nodump "", // exclude_by_ea 0, // hourshift options.get_empty(), true, // alter_atime false, // furtive_read_mode filesystem_ids(false, path("/")), // same_fs comparison_fields::all, // what_to_check false, // snapshot false, // cache_directory_tagging options.get_keep_compressed(), 0, // fixed_date options.get_slice_permission(), 0, // repeat_count 0, // repeat_byte options.get_decremental_mode(), options.get_sequential_marks(), false, // security_check options.get_sparse_file_min_size(), options.get_user_comment(), options.get_hash_algo(), options.get_slice_min_digits(), "", // backup_hook_file_execute bool_mask(false), // backup_hook_file_mask false, // ignore_unknown options.get_fsa_scope(), options.get_multi_threaded_crypto(), options.get_multi_threaded_compress(), options.get_delta_signature(), options.get_has_delta_mask_been_set(), // build delta sig options.get_delta_mask(), // delta_mask options.get_delta_sig_min_size(), false, // delta diff true, // zeroing_neg_date set(), // empty list modified_data_detection::any_inode_change, // not used for merging options.get_iteration_count(), options.get_kdf_hash(), options.get_sig_block_len(), st_ptr); exploitable = false; stack.terminate(); } catch(...) { sauv_path_t.reset(); throw; } sauv_path_t.reset(); } catch(...) { free_mem(); throw; } } archive::i_archive::i_archive(const std::shared_ptr & dialog, const path & chem_src, const string & basename_src, const string & extension_src, const archive_options_read & options_read, const path & chem_dst, const string & basename_dst, const string & extension_dst, const archive_options_repair & options_repair): mem_ui(dialog) { archive_options_read my_options_read = options_read; bool initial_pause = (*options_read.get_entrepot() == *options_repair.get_entrepot() && chem_src == chem_dst); statistics not_filled; //// // initializing object fields // stack will be set by op_create_in_sub() // ver will be set by op_create_in_sub() cat = nullptr; // will be set by op_create_in_sub() exploitable = false; lax_read_mode = false; sequential_read = false; // gnupg_signed is not used while creating an archive // slices will be set by op_create_in_sub() // local_cat_size not used while creating an archive //// // initial parameter setup shared_ptr sauv_path_t = options_repair.get_entrepot(); if(sauv_path_t == nullptr) throw Ememory("archive::i_archive::archive(repair)"); try { sauv_path_t->set_user_ownership(options_repair.get_slice_user_ownership()); sauv_path_t->set_group_ownership(options_repair.get_slice_group_ownership()); sauv_path_t->set_location(chem_dst); tools_avoid_slice_overwriting_regex(get_ui(), *sauv_path_t, basename_dst, extension_dst, options_repair.get_info_details(), options_repair.get_allow_over(), options_repair.get_warn_over(), options_repair.get_empty()); ///// // opening the source archive in sequential read mode my_options_read.set_sequential_read(true); archive src(dialog, chem_src, basename_src, extension_src, my_options_read); if(src.pimpl->cat == nullptr) throw SRC_BUG; op_create_in_sub(oper_repair, chem_dst, sauv_path_t, src.pimpl->cat, // ref1 nullptr, // ref2 initial_pause, bool_mask(true), // selection bool_mask(true), // subtree basename_dst, extension_dst, options_repair.get_allow_over(), crit_constant_action(data_preserve, EA_preserve), // overwrite options_repair.get_warn_over(), options_repair.get_info_details(), options_repair.get_display_treated(), options_repair.get_display_treated_only_dir(), options_repair.get_display_skipped(), options_repair.get_display_finished(), options_repair.get_pause(), false, // empty_dir src.pimpl->ver.get_compression_algo(), 9, // we keep the data compressed this parameter has no importance 0, // we keep the compression block size, this parameter has no importance options_repair.get_slice_size(), options_repair.get_first_slice_size(), bool_mask(true), // ea_mask options_repair.get_execute(), options_repair.get_crypto_algo(), options_repair.get_crypto_pass(), options_repair.get_crypto_size(), options_repair.get_gnupg_recipients(), options_repair.get_gnupg_signatories(), bool_mask(true), // compr_mask 0, // min_compr_size false, // nodump "", // exclude_by_ea 0, // hourshift options_repair.get_empty(), false, // alter_atime false, // furtive_read_mode filesystem_ids(false, path("/")), // same_fs comparison_fields::all, // comparison_fields false, // snapshot false, // cache_directory_tagging, true, // keep_compressed, always 0, // fixed_date options_repair.get_slice_permission(), 0, // repeat_count 0, // repeat_byte false, // decremental true, // tape marks are mandatory in repair mode false, // security_check 0, // sparse_file_min_size (disabling hole detection) options_repair.get_user_comment(), options_repair.get_hash_algo(), options_repair.get_slice_min_digits(), "", // backup_hook_file_execute bool_mask(true), // backup_hook_file_mask false, // ignore_unknown all_fsa_families(), // fsa_scope options_repair.get_multi_threaded_crypto(), options_repair.get_multi_threaded_compress(), true, // delta_signature false, // build_delta_signature bool_mask(true), // delta_mask 0, // delta_sig_min_size false, // delta_diff false, // zeroing_neg_date set(), // ignored_symlinks modified_data_detection::any_inode_change, // not used for repairing src.pimpl->ver.get_iteration_count(), src.pimpl->ver.get_kdf_hash(), delta_sig_block_size(), // sig block size is not used for repairing, build_delta_sig is set to false above ¬_filled); // statistics // stealing src's catalogue, our's is still empty at this step catalogue *tmp = cat; cat = src.pimpl->cat; src.pimpl->cat = tmp; dialog->printf(gettext("Archive repairing completed. WARNING! it is strongly advised to test the resulting archive before removing the damaged one")); } catch(...) { if(!sauv_path_t) sauv_path_t.reset(); throw; } if(!sauv_path_t) sauv_path_t.reset(); } statistics archive::i_archive::op_extract(const path & fs_root, const archive_options_extract & options, statistics * progressive_report) { statistics st = false; // false => no lock for this internal object statistics *st_ptr = progressive_report == nullptr ? &st : progressive_report; comparison_fields wtc = options.get_what_to_check(); path real_fs_root("."); try { // sanity checks if(!exploitable) throw Elibcall("op_extract", gettext("This archive is not exploitable, check documentation for more")); check_against_isolation(lax_read_mode); // this avoid to try extracting archive directly from an isolated catalogue // the isolated catalogue can be used to extract data (since format "08") but only // associated with the real plain archive, not alone. // end of sanity checks if(wtc == comparison_fields::all && capability_CHOWN(get_ui(), options.get_info_details()) == capa_clear && getuid() != 0) { wtc = comparison_fields::ignore_owner; get_ui().pause(gettext("File ownership will not be restored du to the lack of privilege, you can disable this message by asking not to restore file ownership")); } fs_root.explode_undisclosed(); enable_natural_destruction(); if(options.get_in_place()) { if(!get_cat().get_in_place(real_fs_root)) throw Erange("op_extract", gettext("Cannot use in-place restoration as no in-place path is stored in the archive")); } else real_fs_root = fs_root; /// calculating and setting the " recursive_has_changed" fields of directories to their values if(options.get_empty_dir() == false) get_cat().launch_recursive_has_changed_update(); /// we can now use the cat_directory::get_recursive_has_changed() to avoid recursion in a directory where /// no file has been saved. try { filtre_restore(get_pointer(), options.get_selection(), options.get_subtree(), get_cat(), tools_relative2absolute_path(real_fs_root, tools_getcwd()), options.get_warn_over(), options.get_info_details(), options.get_display_treated(), options.get_display_treated_only_dir(), options.get_display_skipped(), *st_ptr, options.get_ea_mask(), options.get_flat(), wtc, options.get_warn_remove_no_match(), options.get_empty(), options.get_empty_dir(), options.get_overwriting_rules(), options.get_dirty_behavior(), options.get_only_deleted(), options.get_ignore_deleted(), options.get_fsa_scope(), options.get_ignore_unix_sockets()); } catch(Euser_abort & e) { disable_natural_destruction(); throw; } catch(Ethread_cancel & e) { disable_natural_destruction(); throw; } catch(Erange &e) { string msg = string(gettext("Error while restoring data: ")) + e.get_message(); get_ui().message(msg); throw Edata(msg); } } catch(...) { if(sequential_read) exploitable = false; throw; } if(sequential_read) exploitable = false; return *st_ptr; } void archive::i_archive::summary() { try { archive_summary sum = summary_data(); infinint tmp; get_header().display(get_ui()); tmp = sum.get_catalog_size(); if(!tmp.is_zero()) get_ui().printf(gettext("Catalogue size in archive : %i bytes"), &tmp); else get_ui().printf(gettext("Catalogue size in archive : N/A")); get_ui().printf(""); tmp = sum.get_slice_number(); if(!tmp.is_zero()) { get_ui().printf(gettext("Archive is composed of %i file(s)"), &tmp); if(tmp == 1) { tmp = sum.get_last_slice_size(); get_ui().printf(gettext("File size: %i bytes"), &tmp); } else { infinint first = sum.get_first_slice_size(); infinint slice = sum.get_slice_size(); infinint last = sum.get_last_slice_size(); infinint total = sum.get_archive_size(); if(first != slice) get_ui().printf(gettext("First file size : %i bytes"), &first); get_ui().printf(gettext("File size : %i bytes"), &slice); get_ui().printf(gettext("Last file size : %i bytes"), &last); get_ui().printf(gettext("Archive total size is : %i bytes"), &total); } } else // not reading from a sar { tmp = sum.get_archive_size(); if(!tmp.is_zero()) { get_ui().printf(gettext("Archive size is: %i bytes"), &tmp); get_ui().printf(gettext("Previous archive size does not include headers present in each slice")); } else get_ui().printf(gettext("Archive size is unknown (reading from a pipe)")); } if(sum.get_data_size() < sum.get_storage_size()) { infinint delta = sum.get_storage_size() - sum.get_data_size(); get_ui().printf(gettext("The overall archive size includes %i byte(s) wasted due to bad compression ratio"), &delta); } else { if(!sum.get_storage_size().is_zero()) get_ui().message(string(gettext("The global data compression ratio is: ")) + tools_get_compression_ratio(sum.get_storage_size(), sum.get_data_size(), true)); } if(only_contains_an_isolated_catalogue()) { infinint first = sum.get_ref_first_slice_size(); infinint other = sum.get_ref_slice_size(); get_ui().printf(gettext("\nWARNING! This archive only contains the catalogue of another archive, it can only be used as reference for differential backup or as rescue in case of corruption of the original archive's content. You cannot restore any data from this archive alone\n")); get_ui().printf(""); get_ui().printf("Archive of reference slicing:"); if(other.is_zero()) get_ui().printf(gettext("\tUnknown or no slicing")); else { if(first != other && !first.is_zero()) get_ui().printf(gettext("\tFirst slice : %i byte(s)"), &first); get_ui().printf(gettext("\tOther slices: %i byte(s)"), &other); } get_ui().printf(""); } string in_place = sum.get_in_place(); if(!in_place.empty()) get_ui().printf(gettext("in-place path: %S"), & in_place); else get_ui().message(gettext("no in-place path recorded")); sum.get_contents().listing(get_ui()); } catch(...) { if(sequential_read) exploitable = false; throw; } if(sequential_read) exploitable = false; } archive_summary archive::i_archive::summary_data() { archive_summary ret; infinint sub_slice_size; infinint first_slice_size; infinint last_slice_size; infinint slice_number; infinint archive_size; slice_layout slyt; path tmp("."); // sanity checks if(!exploitable) throw Elibcall("summary", gettext("This archive is not exploitable, check the archive class usage in the API documentation")); // end of sanity checks try { if(!get_catalogue_slice_layout(slyt)) slyt.clear(); if(get_sar_param(sub_slice_size, first_slice_size, last_slice_size, slice_number)) { if(slice_number == 1) { sub_slice_size = last_slice_size; first_slice_size = last_slice_size; archive_size = last_slice_size; } if(slice_number > 1) archive_size = first_slice_size + (slice_number - 2)*sub_slice_size + last_slice_size; else archive_size = 0; // unknown } else // not reading from a sar { archive_size = get_level2_size(); (void)archive_size.is_zero(); sub_slice_size = 0; first_slice_size = 0; last_slice_size = 0; slice_number = 0; } } catch(Erange & e) { get_ui().message(e.get_message()); sub_slice_size = 0; first_slice_size = 0; last_slice_size = 0; slice_number = 0; archive_size = get_level2_size(); } ret.set_slice_size(sub_slice_size); ret.set_first_slice_size(first_slice_size); ret.set_last_slice_size(last_slice_size); ret.set_ref_slice_size(slyt.other_size); ret.set_ref_first_slice_size(slyt.first_size); ret.set_slice_number(slice_number); ret.set_archive_size(archive_size); ret.set_catalog_size(get_cat_size()); ret.set_edition(get_header().get_edition().display()); ret.set_compression_algo(compression2string(get_header().get_compression_algo())); ret.set_user_comment(get_header().get_command_line()); ret.set_cipher(get_header().get_sym_crypto_name()); ret.set_asym(get_header().get_asym_crypto_name()); ret.set_signed(get_header().is_signed()); ret.set_tape_marks(get_header().get_tape_marks()); if(get_cat().get_contenu() == nullptr) throw SRC_BUG; ret.set_storage_size(get_cat().get_contenu()->get_storage_size()); ret.set_data_size(get_cat().get_contenu()->get_size()); if(get_cat().get_in_place(tmp)) { if(tmp.is_relative()) throw SRC_BUG; ret.set_in_place(tmp.display()); } else ret.set_in_place(""); // empty string when in_place is not set ret.set_contents(get_cat().get_stats()); return ret; } void archive::i_archive::op_listing(archive_listing_callback callback, void *context, const archive_options_listing & options) const { i_archive *me = const_cast(this); if(me == nullptr) throw SRC_BUG; if(callback == nullptr) throw Elibcall("archive::op_listing", "null pointer given as callback function for archive listing"); try { slice_layout used_layout; thread_cancellation thr; if(options.get_display_ea() && sequential_read) throw Erange("archive::i_archive::get_children_of", gettext("Fetching EA value while listing an archive is not possible in sequential read mode")); if(options.get_slicing_location()) // -Tslice is asked { if(!only_contains_an_isolated_catalogue() && sequential_read) throw Erange("archive::i_archive::op_listing", gettext("slicing focused output is not available in sequential-read mode")); if(!get_catalogue_slice_layout(used_layout)) { if(options.get_user_slicing(used_layout.first_size, used_layout.other_size)) { if(options.get_info_details()) get_ui().printf(gettext("Using user provided modified slicing (first slice = %i bytes, other slices = %i bytes)"), &used_layout.first_size, &used_layout.other_size); } else throw Erange("archive::i_archive::op_listing", gettext("No slice layout of the archive of reference for the current isolated catalogue is available, cannot provide slicing information, aborting")); } } if(options.get_filter_unsaved()) get_cat().launch_recursive_has_changed_update(); me->enable_natural_destruction(); try { const cat_entree *e = nullptr; const cat_nomme *e_nom = nullptr; const cat_inode *e_ino = nullptr; const cat_directory *e_dir = nullptr; const cat_eod *e_eod = nullptr; const cat_mirage *e_mir = nullptr; const cat_eod tmp_eod; thread_cancellation thr; defile juillet = FAKE_ROOT; list_entry ent; bool isolated; if(exploitable) isolated = only_contains_an_isolated_catalogue(); else isolated = false; get_cat().reset_read(); while(get_cat().read(e)) { e_nom = dynamic_cast(e); e_ino = dynamic_cast(e); e_dir = dynamic_cast(e); e_eod = dynamic_cast(e); e_mir = dynamic_cast(e); if(e == nullptr) throw SRC_BUG; thr.check_self_cancellation(); juillet.enfile(e); if(options.get_subtree().is_covered(juillet.get_path()) && (e_dir != nullptr || e_nom == nullptr || options.get_selection().is_covered(e_nom->get_name()))) { if(e_mir != nullptr) e_ino = e_mir->get_inode(); if(!options.get_filter_unsaved() // invoking callback if not filtering unsaved || e_eod != nullptr // invoking callback for all eod || e->get_saved_status() == saved_status::saved // invoking callback for file having data saved || e->get_saved_status() == saved_status::delta // invoking callback for file having a delta patch as data || (e_ino != nullptr && e_ino->ea_get_saved_status() == ea_saved_status::full) // invoking callback for files having EA saved || (e_ino != nullptr && e_ino->ea_get_saved_status() == ea_saved_status::fake) // invoking callback for saved in recorded in isolated catalogue || (e_dir != nullptr && e_dir->get_recursive_has_changed()) // invoking callback for directory containing saved files ) { e->set_list_entry(&used_layout, options.get_display_ea(), ent); if(local_check_dirty_seq(get_cat().get_escape_layer())) ent.set_dirtiness(true); if(isolated && (e->get_saved_status() == saved_status::saved || e->get_saved_status() == saved_status::delta)) ent.set_saved_status(saved_status::fake); try { callback(juillet.get_string_without_root(), ent, context); } catch(Egeneric & e) { throw Elibcall("archive::i_archive::op_listing", tools_printf(gettext("Exception caught from archive_listing_callback execution: %s"), e.dump_str().c_str())); } catch(...) { throw Elibcall("archive::i_archive::op_listing", gettext("Exception caught from archive_listing_callback execution")); } } else // not saved, filtered out { if(e_dir != nullptr) { get_cat().skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } } } else // filtered out { if(e_dir != nullptr) { get_cat().skip_read_to_parent_dir(); juillet.enfile(&tmp_eod); } } } } catch(Euser_abort & e) { me->disable_natural_destruction(); throw; } catch(Erange & e) { string msg = string(gettext("Error while listing archive contents: ")) + e.get_message(); throw Edata(msg); } } catch(...) { if(sequential_read) me->exploitable = false; throw; } if(sequential_read) me->exploitable = false; } statistics archive::i_archive::op_diff(const path & fs_root, const archive_options_diff & options, statistics * progressive_report) { statistics st = false; // false => no lock for this internal object statistics *st_ptr = progressive_report == nullptr ? &st : progressive_report; bool isolated_mode = false; path real_fs_root("."); try { // sanity checks if(!exploitable) throw Elibcall("op_diff", gettext("This archive is not exploitable, check documentation for more")); try { check_against_isolation(lax_read_mode); } catch(Erange & e) { get_ui().message("This archive contains an isolated catalogue, only meta data can be used for comparison, CRC will be used to compare data of delta signature if present. Warning: Succeeding this comparison does not mean there is no difference as two different files may have the same CRC or the same delta signature"); isolated_mode = true; } // end of sanity checks fs_root.explode_undisclosed(); enable_natural_destruction(); if(options.get_in_place()) { if(!get_cat().get_in_place(real_fs_root)) throw Erange("op_extract", gettext("Cannot use in-place restoration as no in-place path is stored in the archive")); } else real_fs_root = fs_root; try { filtre_difference(get_pointer(), options.get_selection(), options.get_subtree(), get_cat(), tools_relative2absolute_path(real_fs_root, tools_getcwd()), options.get_info_details(), options.get_display_treated(), options.get_display_treated_only_dir(), options.get_display_skipped(), *st_ptr, options.get_ea_mask(), options.get_alter_atime(), options.get_furtive_read_mode(), options.get_what_to_check(), options.get_hourshift(), options.get_compare_symlink_date(), options.get_fsa_scope(), isolated_mode); } catch(Euser_abort & e) { disable_natural_destruction(); throw; } catch(Ethread_cancel & e) { disable_natural_destruction(); throw; } catch(Erange & e) { string msg = string(gettext("Error while comparing archive with filesystem: "))+e.get_message(); get_ui().message(msg); throw Edata(msg); } } catch(...) { if(sequential_read) exploitable = false; throw; } if(sequential_read) exploitable = false; return *st_ptr; } statistics archive::i_archive::op_test(const archive_options_test & options, statistics * progressive_report) { statistics st = false; // false => no lock for this internal object statistics *st_ptr = progressive_report == nullptr ? &st : progressive_report; bool isolated = false; try { // sanity checks if(!exploitable) throw Elibcall("op_test", gettext("This archive is not exploitable, check the archive class usage in the API documentation")); try { check_against_isolation(lax_read_mode); } catch(Erange & e) { // no data/EA are available in this archive, // we can return normally at this point // as the catalogue has already been read // thus all that could be tested has been tested get_ui().message(gettext("WARNING! This is an isolated catalogue, no data or EA is present in this archive, only the catalogue structure can be checked")); isolated = true; } // end of sanity checks enable_natural_destruction(); try { try { if(isolated) { const cat_entree *tmp; if(cat == nullptr) throw SRC_BUG; cat->read(tmp); // should be enough to have the whole catalogue being read if using sequential read mode cat->reset_read(); } else filtre_test(get_pointer(), options.get_selection(), options.get_subtree(), get_cat(), options.get_info_details(), options.get_display_treated(), options.get_display_treated_only_dir(), options.get_display_skipped(), options.get_empty(), *st_ptr); } catch(Erange & e) { get_ui().message(gettext("A problem occurred while reading this archive contents: ") + e.get_message()); } } catch(Euser_abort & e) { disable_natural_destruction(); throw; } catch(Ethread_cancel & e) { disable_natural_destruction(); throw; } catch(Erange & e) { string msg = string(gettext("Error while testing archive: "))+e.get_message(); get_ui().message(msg); throw Edata(msg); } } catch(...) { if(sequential_read) exploitable = false; throw; } if(sequential_read) exploitable = false; return *st_ptr; } void archive::i_archive::op_isolate(const path &sauv_path, const string & filename, const string & extension, const archive_options_isolate & options) { shared_ptr sauv_path_t = options.get_entrepot(); if(sauv_path_t == nullptr) throw Ememory("archive::i_archive::archive"); sauv_path_t->set_user_ownership(options.get_slice_user_ownership()); sauv_path_t->set_group_ownership(options.get_slice_group_ownership()); sauv_path_t->set_location(sauv_path); tools_avoid_slice_overwriting_regex(get_ui(), *sauv_path_t, filename, extension, options.get_info_details(), options.get_allow_over(), options.get_warn_over(), options.get_empty()); try { pile layers; header_version isol_ver; label isol_data_name; label internal_name; slice_layout isol_slices; // this field is not used here, but necessary to call macro_tools_create_layers() if(!exploitable && options.get_delta_signature()) throw Erange("archive::i_archive::op_isolate", gettext("Isolation with delta signature is not possible on a just created archive (on-fly isolation)")); do { isol_data_name.generate_internal_filename(); } while(isol_data_name == cat->get_data_name()); internal_name = isol_data_name; macro_tools_create_layers(get_pointer(), layers, isol_ver, isol_slices, &slices, // giving our slice_layout as reference to be stored in the archive header/trailer sauv_path_t, filename, extension, options.get_allow_over(), options.get_warn_over(), options.get_info_details(), options.get_pause(), options.get_compression(), options.get_compression_level(), options.get_compression_block_size(), options.get_slice_size(), options.get_first_slice_size(), options.get_execute(), options.get_crypto_algo(), options.get_crypto_pass(), options.get_crypto_size(), options.get_gnupg_recipients(), options.get_gnupg_signatories(), options.get_empty(), options.get_slice_permission(), options.get_sequential_marks(), options.get_user_comment(), options.get_hash_algo(), options.get_slice_min_digits(), internal_name, isol_data_name, options.get_iteration_count(), options.get_kdf_hash(), options.get_multi_threaded_crypto(), options.get_multi_threaded_crypto()); /* must be changed with dedicated field for compression */ if(cat == nullptr) throw SRC_BUG; // note: // an isolated catalogue keeps the data, EA and FSA pointers of the archive they come from // but have their own copy of the delta signature if present, in order to be able to make // a differential/incremental delta binary based on an isolated catalogue. The drawback is // that reading an archive with the help of an isolated catalogue, the delta signature used // will always be those of the isolated catalogue, not those of the archive the catalogue // has been isolated from. // Using an isolated catalogue as backup of the internal catalogue stays possible but not // for the delta_signature which are ignored by libdar in that situation (archive reading). // However as delta_signature are only used at archive creation time this does not hurt. Delta // signature will be available during a differential backup either from the isolated catalogue // using its own copy of them, or from the original archive which would contain their original // version. if(options.get_delta_signature()) { pile_descriptor pdesc = & layers; cat->transfer_delta_signatures(pdesc, sequential_read, options.get_has_delta_mask_been_set(), options.get_delta_mask(), options.get_delta_sig_min_size(), options.get_sig_block_len()); } else // we drop the delta signature as they will never be used // because none will be in the isolated catalogue // and that an isolated catalogue as backup of an internal // catalogue cannot rescue the delta signature (they have // to be in the isolated catalogue) cat->drop_delta_signatures(); if(isol_data_name == cat->get_data_name()) throw SRC_BUG; // data_name generated just above by slice layer // should never equal the data_name of the catalogue // when performing isolation macro_tools_close_layers(get_pointer(), layers, isol_ver, *cat, options.get_info_details(), options.get_crypto_algo(), options.get_compression(), options.get_gnupg_recipients(), options.get_gnupg_signatories(), options.get_empty()); } catch(...) { sauv_path_t.reset(); throw; } sauv_path_t.reset(); } void archive::i_archive::load_catalogue() { if(exploitable && sequential_read) // the catalogue is not even yet read, so we must first read it entirely { if(only_contains_an_isolated_catalogue()) // this is easy... asking just an entry //from the catalogue makes its whole being read { const cat_entree *tmp; if(cat == nullptr) throw SRC_BUG; cat->read(tmp); // should be enough to have the whole catalogue being read cat->reset_read(); } else // here we have a plain archive, doing the test operation // is the simplest way to read the whole archive and thus get its contents // (i.e.: the catalogue) (void)op_test(archive_options_test(), nullptr); } } bool archive::i_archive::get_children_of(archive_listing_callback callback, void *context, const string & dir, bool fetch_ea) { bool ret; if(callback == nullptr) throw Erange("archive::i_archive::get_children_of", "nullptr provided as user callback function"); if(fetch_ea && sequential_read) throw Erange("archive::i_archive::get_children_of", gettext("Fetching EA value while listing an archive is not possible in sequential read mode")); load_catalogue(); // OK, now that we have the whole catalogue available in memory, let's rock! vector tmp = get_children_in_table(dir,fetch_ea); vector::iterator it = tmp.begin(); ret = (it != tmp.end()); while(it != tmp.end()) { try { callback(dir, *it, context); } catch(...) { throw Elibcall("archive::i_archive::get_children_of", "user provided callback function should not throw any exception"); } } return ret; } const vector archive::i_archive::get_children_in_table(const string & dir, bool fetch_ea) const { vector ret; i_archive* me = const_cast(this); if(me == nullptr) throw SRC_BUG; if(fetch_ea && sequential_read) throw Erange("archive::i_archive::get_children_of", gettext("Fetching EA value while listing an archive is not possible in sequential read mode")); me->load_catalogue(); const cat_directory* parent = get_dir_object(dir); const cat_nomme* tmp_ptr = nullptr; list_entry ent; if(parent == nullptr) throw SRC_BUG; else { try { U_I sz = 0; infinint i_sz = parent->get_dir_size(); i_sz.unstack(sz); // if i_sz > 0 the requested size is larger than what U_I can hold // we won't handle this case here as we have not mean to // ask the stdc++ lib to reserver as much memory // though, there is chances that the stdc++ library // will not be able to get as much memory and will // throw a std::bad_alloc exception when calling reserve() ret.reserve(sz); } catch(std::bad_alloc &) { throw Ememory("std::vector::reserve()"); } } parent->reset_read_children(); while(parent->read_children(tmp_ptr)) { if(tmp_ptr == nullptr) throw SRC_BUG; tmp_ptr->set_list_entry(&slices, fetch_ea, ent); // fill a new entry in the table ret.push_back(ent); } return ret; } bool archive::i_archive::has_subdirectory(const string & dir) const { bool ret = false; const cat_directory *parent = get_dir_object(dir); const cat_nomme *tmp_ptr = nullptr; parent->reset_read_children(); while(parent->read_children(tmp_ptr) && !ret) { if(dynamic_cast(tmp_ptr) != nullptr) ret = true; } return ret; } void archive::i_archive::init_catalogue() const { if(exploitable && sequential_read) // the catalogue is not even yet read, so we must first read it entirely { if(only_contains_an_isolated_catalogue()) // this is easy... asking just an entry //from the catalogue makes its whole being read { const cat_entree *tmp; if(cat == nullptr) throw SRC_BUG; cat->read(tmp); // should be enough to have the whole catalogue being read cat->reset_read(); } else { if(cat == nullptr) throw SRC_BUG; filtre_sequentially_read_all_catalogue(*cat, get_pointer(), lax_read_mode); } } if(cat == nullptr) throw SRC_BUG; } const catalogue & archive::i_archive::get_catalogue() const { if(exploitable && sequential_read) throw Elibcall("archive::i_archive::get_catalogue", "Reading the first time the catalogue of an archive open in sequential read mode needs passing a \"user_interaction\" object to the argument of archive::i_archive::get_catalogue or call init_catalogue() first "); if(cat == nullptr) throw SRC_BUG; return *cat; } void archive::i_archive::drop_all_filedescriptors() { if(exploitable && sequential_read) { if(only_contains_an_isolated_catalogue()) { const cat_entree *tmp; if(cat == nullptr) throw SRC_BUG; cat->read(tmp); // should be enough to have the whole catalogue being read cat->reset_read(); } else (void)op_test(archive_options_test(), nullptr); } stack.clear(); exploitable = false; } bool archive::i_archive::get_catalogue_slice_layout(slice_layout & slicing) const { slicing = slices; // by default we use the slice layout of the current archive, // this is modified if the current archive is an isolated catalogue // in archive format 9 or greater, then we use the slice layout contained // in the archive header/trailer which is a copy of the one of the archive of reference // a warning is issued in that case if(only_contains_an_isolated_catalogue()) { if(ver.get_slice_layout() != nullptr) { slicing = *ver.get_slice_layout(); return true; } else // no slicing of the archive of reference stored in this isolated catalogue's header/trailer { if(ver.get_edition() >= 9) throw SRC_BUG; // starting revision 9 isolated catalogue should always contain // the slicing of the archive of reference, even if that reference is using an archive format // older than version 9. return false; } } else return true; } U_64 archive::i_archive::get_first_slice_header_size() const { U_64 ret; infinint pre_ret; const generic_file *bottom = stack.bottom(); const trivial_sar *b_triv = dynamic_cast(bottom); const sar *b_sar = dynamic_cast(bottom); const zapette *b_zap = dynamic_cast(bottom); if(b_triv != nullptr) pre_ret = b_triv->get_slice_header_size(); else if(b_sar != nullptr) pre_ret = b_sar->get_first_slice_header_size(); else if(b_zap != nullptr) pre_ret = b_zap->get_first_slice_header_size(); else pre_ret = 0; // unknown size if(!tools_infinint2U_64(pre_ret, ret)) ret = 0; return ret; } U_64 archive::i_archive::get_non_first_slice_header_size() const { U_64 ret; infinint pre_ret; const generic_file *bottom = stack.bottom(); const trivial_sar *b_triv = dynamic_cast(bottom); const sar *b_sar = dynamic_cast(bottom); const zapette *b_zap = dynamic_cast(bottom); if(b_triv != nullptr) pre_ret = b_triv->get_slice_header_size(); else if(b_sar != nullptr) pre_ret = b_sar->get_non_first_slice_header_size(); else if(b_zap != nullptr) pre_ret = b_zap->get_non_first_slice_header_size(); else pre_ret = 0; // unknown size if(!tools_infinint2U_64(pre_ret, ret)) ret = 0; return ret; } //////////////////// // PRIVATE METHODS FOLLOW // statistics archive::i_archive::op_create_in(operation op, const path & fs_root, const shared_ptr & sauv_path_t, archive *ref_arch, const mask & selection, const mask & subtree, const string & filename, const string & extension, bool allow_over, bool warn_over, bool info_details, bool display_treated, bool display_treated_only_dir, bool display_skipped, bool display_finished, const infinint & pause, bool empty_dir, compression algo, U_I compression_level, U_I compression_block_size, const infinint & file_size, const infinint & first_file_size, const mask & ea_mask, const string & execute, crypto_algo crypto, const secu_string & pass, U_32 crypto_size, const vector & gnupg_recipients, const vector & gnupg_signatories, const mask & compr_mask, const infinint & min_compr_size, bool nodump, const string & exclude_by_ea, const infinint & hourshift, bool empty, bool alter_atime, bool furtive_read_mode, const filesystem_ids & same_fs, comparison_fields what_to_check, bool snapshot, bool cache_directory_tagging, const infinint & fixed_date, const string & slice_permission, const infinint & repeat_count, const infinint & repeat_byte, bool add_marks_for_sequential_reading, bool security_check, const infinint & sparse_file_min_size, const string & user_comment, hash_algo hash, const infinint & slice_min_digits, const string & backup_hook_file_execute, const mask & backup_hook_file_mask, bool ignore_unknown, const fsa_scope & scope, U_I multi_threaded_crypto, U_I multi_threaded_compress, bool delta_signature, bool build_delta_sig, const mask & delta_mask, const infinint & delta_sig_min_size, bool delta_diff, bool zeroing_neg_date, const set & ignored_symlinks, modified_data_detection mod_data_detect, const infinint & iteration_count, hash_algo kdf_hash, const delta_sig_block_size & sig_block_len, statistics * progressive_report) { statistics st = false; // false => no lock for this internal object statistics *st_ptr = progressive_report == nullptr ? &st : progressive_report; // sanity checks as much as possible to avoid libdar crashing due to bad arguments // useless arguments are not reported. if((compression_level > 9 && algo != compression::zstd) || compression_level < 1) throw Elibcall("op_create_in", gettext("Compression_level must be between 1 and 9 included")); if(file_size.is_zero() && !first_file_size.is_zero()) throw Elibcall("op_create_in", gettext("\"first_file_size\" cannot be different from zero if \"file_size\" is equal to zero")); if(crypto_size < 10 && crypto != crypto_algo::none) throw Elibcall("op_create_in", gettext("Crypto block size must be greater than 10 bytes")); #ifndef LIBDAR_NODUMP_FEATURE if(nodump) throw Ecompilation(gettext("nodump flag feature has not been activated at compilation time, it is thus not available")); #endif check_libgcrypt_hash_bug(get_ui(), hash, first_file_size, file_size); // end of sanity checks fs_root.explode_undisclosed(); catalogue *ref_cat = nullptr; bool initial_pause = false; path sauv_path_abs = sauv_path_t->get_location(); path fs_root_abs = fs_root.is_relative() ? tools_relative2absolute_path(fs_root, tools_getcwd()) : fs_root; if(sauv_path_abs.is_relative()) sauv_path_abs = sauv_path_t->get_root() + sauv_path_abs; tools_avoid_slice_overwriting_regex(get_ui(), *sauv_path_t, filename, extension, info_details, allow_over, warn_over, empty); if(ref_arch != nullptr && ref_arch->pimpl->sequential_read && (delta_signature || delta_diff)) throw Erange("archive::i_archive::op_create_in", gettext("Cannot sequentially read an archive of reference when delta signature or delta patch is requested")); local_cat_size = 0; // unknown catalogue size (need to re-open the archive, once creation has completed) [object member variable] sauv_path_abs.explode_undisclosed(); // warning against saving the archive itself const entrepot_local *sauv_path_t_local = dynamic_cast(sauv_path_t.get()); if(op == oper_create && sauv_path_t_local != nullptr // not using a remote storage && sauv_path_abs.is_subdir_of(fs_root_abs, true) && selection.is_covered(filename+".1."+extension) && subtree.is_covered(sauv_path_abs.append(filename+".1."+extension)) && filename!= "-") { bool cov = true; // whether the archive is covered by filter (this is saving itself) string drop; // will carry the removed part of the sauv_path_abs variable // checking for exclusion due to different filesystem if(! same_fs.is_covered(sauv_path_abs)) cov = false; if(snapshot) // if we do a snapshot we dont create an archive this no risk to save ourselves cov = false; // checking for directory auto inclusion do { cov = cov && subtree.is_covered(sauv_path_abs); } while(cov && sauv_path_abs.pop(drop)); if(cov) get_ui().pause(tools_printf(gettext("WARNING! The archive is located in the directory to backup, this may create an endless loop when the archive will try to save itself. You can either add -X \"%S.*.%S\" on the command line, or change the location of the archive (see -h for help). Do you really want to continue?"), &filename, &extension)); } // building the reference catalogue if(ref_arch != nullptr) // from a existing archive { const shared_ptr ref_where = ref_arch->pimpl->get_entrepot(); if(ref_where) initial_pause = (*ref_where == *sauv_path_t); ref_cat = const_cast(& ref_arch->pimpl->get_catalogue()); } op_create_in_sub(op, fs_root, sauv_path_t, ref_cat, nullptr, initial_pause, selection, subtree, filename, extension, allow_over, allow_over ? crit_constant_action(data_overwrite, EA_overwrite) : crit_constant_action(data_preserve, EA_preserve), // we do not have any overwriting policy in this environement (archive creation and isolation), so we create one on-fly warn_over, info_details, display_treated, display_treated_only_dir, display_skipped, display_finished, pause, empty_dir, algo, compression_level, compression_block_size, file_size, first_file_size, ea_mask, execute, crypto, pass, crypto_size, gnupg_recipients, gnupg_signatories, compr_mask, min_compr_size, nodump, exclude_by_ea, hourshift, empty, alter_atime, furtive_read_mode, same_fs, what_to_check, snapshot, cache_directory_tagging, false, // keep_compressed fixed_date, slice_permission, repeat_count, repeat_byte, false, // decremental mode add_marks_for_sequential_reading, security_check, sparse_file_min_size, user_comment, hash, slice_min_digits, backup_hook_file_execute, backup_hook_file_mask, ignore_unknown, scope, multi_threaded_crypto, multi_threaded_compress, delta_signature, build_delta_sig, delta_mask, delta_sig_min_size, delta_diff, zeroing_neg_date, ignored_symlinks, mod_data_detect, iteration_count, kdf_hash, sig_block_len, st_ptr); return *st_ptr; } void archive::i_archive::op_create_in_sub(operation op, const path & fs_root, const shared_ptr & sauv_path_t, catalogue *ref_cat1, const catalogue *ref_cat2, bool initial_pause, const mask & selection, const mask & subtree, const string & filename, const string & extension, bool allow_over, const crit_action & overwrite, bool warn_over, bool info_details, bool display_treated, bool display_treated_only_dir, bool display_skipped, bool display_finished, const infinint & pause, bool empty_dir, compression algo, U_I compression_level, U_I compression_block_size, const infinint & file_size, const infinint & first_file_size, const mask & ea_mask, const string & execute, crypto_algo crypto, const secu_string & pass, U_32 crypto_size, const vector & gnupg_recipients, const vector & gnupg_signatories, const mask & compr_mask, const infinint & min_compr_size, bool nodump, const string & exclude_by_ea, const infinint & hourshift, bool empty, bool alter_atime, bool furtive_read_mode, const filesystem_ids & same_fs, comparison_fields what_to_check, bool snapshot, bool cache_directory_tagging, bool keep_compressed, const infinint & fixed_date, const string & slice_permission, const infinint & repeat_count, const infinint & repeat_byte, bool decremental, bool add_marks_for_sequential_reading, bool security_check, const infinint & sparse_file_min_size, const string & user_comment, hash_algo hash, const infinint & slice_min_digits, const string & backup_hook_file_execute, const mask & backup_hook_file_mask, bool ignore_unknown, const fsa_scope & scope, U_I multi_threaded_crypto, U_I multi_threaded_compress, bool delta_signature, bool build_delta_sig, const mask & delta_mask, const infinint & delta_sig_min_size, bool delta_diff, bool zeroing_neg_date, const set & ignored_symlinks, modified_data_detection mod_data_detect, const infinint & iteration_count, hash_algo kdf_hash, const delta_sig_block_size & sig_block_len, statistics * st_ptr) { try { stack.clear(); // [object member variable] bool aborting = false; infinint aborting_next_etoile = 0; U_64 flag = 0; // carries the sar option flag label internal_name; generic_file *tmp = nullptr; thread_cancellation thr_cancel; if(ref_cat1 == nullptr && op != oper_create) throw SRC_BUG; if(st_ptr == nullptr) throw SRC_BUG; secu_string real_pass = pass; internal_name.generate_internal_filename(); try { // pausing if saving in the same directory where is located the archive of reference if(!pause.is_zero() && initial_pause) get_ui().pause(gettext("Ready to start writing down the archive?")); macro_tools_create_layers(get_pointer(), stack, // this object field is set! ver, // this object field is set! slices,// this object field is set! nullptr, // no slicing reference stored in archive header/trailer sauv_path_t, filename, extension, allow_over, warn_over, info_details, pause, algo, compression_level, compression_block_size, file_size, first_file_size, execute, crypto, pass, crypto_size, gnupg_recipients, gnupg_signatories, empty, slice_permission, add_marks_for_sequential_reading, user_comment, hash, slice_min_digits, internal_name, internal_name, // data_name is equal to internal_name in the current situation iteration_count, kdf_hash, multi_threaded_crypto, multi_threaded_compress); // ********** building the catalogue (empty for now) ************************* // datetime root_mtime; pile_descriptor pdesc(&stack); crit_action* rep_decr = nullptr; // not used, just needed to pass as argumen to filtre_merge_step0() const crit_action *rep_over = &overwrite; // not used, just needed to pass as argumen to filtre_merge_step0() cat = nullptr; // [object member variable] if(info_details) get_ui().message(gettext("Building the catalog object...")); try { if(fs_root.display() != "") root_mtime = tools_get_mtime(get_ui(), fs_root.display(), zeroing_neg_date, false); // not silent else // case of merging operation for example { datetime mtime1 = ref_cat1 != nullptr ? ref_cat1->get_root_mtime() : datetime(0); datetime mtime2 = ref_cat2 != nullptr ? ref_cat2->get_root_mtime() : datetime(0); root_mtime = mtime1 > mtime2 ? mtime1 : mtime2; } } catch(Erange & e) { string tmp = fs_root.display(); throw Erange("archive::i_archive::op_create_in_sub", tools_printf(gettext("Error while fetching information for %S: "), &tmp) + e.get_message()); } switch(op) { case oper_merge: case oper_repair: if(add_marks_for_sequential_reading && !empty) cat = new (nothrow) escape_catalogue(get_pointer(), pdesc, ref_cat1->get_root_dir_last_modif(), internal_name); else cat = new (nothrow) catalogue(get_pointer(), ref_cat1->get_root_dir_last_modif(), internal_name); break; case oper_create: if(add_marks_for_sequential_reading && !empty) cat = new (nothrow) escape_catalogue(get_pointer(), pdesc, root_mtime, internal_name); else cat = new (nothrow) catalogue(get_pointer(), root_mtime, internal_name); break; default: throw SRC_BUG; } if(cat == nullptr) throw Ememory("archive::i_archive::op_create_in_sub"); // *********** now we can perform the data filtering operation (adding data to the archive) *************** // path ref1_in_place("."); path ref2_in_place("."); try { catalogue *void_cat = nullptr; const catalogue *ref_cat_ptr = ref_cat1; switch(op) { case oper_create: // setting in_place cat->set_in_place(fs_root); if(ref_cat1 == nullptr) { // using a empty catalogue as reference if no reference is given label data_name; data_name.clear(); void_cat = new (nothrow) catalogue(get_pointer(), datetime(0), data_name); if(void_cat == nullptr) throw Ememory("archive::i_archive::op_create_in_sub"); ref_cat_ptr = void_cat; } else { if(ref_cat1->get_in_place(ref1_in_place)) { if(ref1_in_place != fs_root) { string ref1 = ref1_in_place.display(); string actual = fs_root.display(); get_ui().printf(gettext("Warning making a differential/incremental backup with a different root directory: %S <-> %S"), &ref1, &actual); } // else we cannot check (old archive format or merged archive } } try { if(info_details) get_ui().message(gettext("Processing files for backup...")); filtre_sauvegarde(get_pointer(), selection, subtree, pdesc, *cat, *ref_cat_ptr, fs_root, info_details, display_treated, display_treated_only_dir, display_skipped, display_finished, *st_ptr, empty_dir, ea_mask, compr_mask, min_compr_size, nodump, hourshift, alter_atime, furtive_read_mode, same_fs, what_to_check, snapshot, cache_directory_tagging, security_check, repeat_count, repeat_byte, fixed_date, sparse_file_min_size, backup_hook_file_execute, backup_hook_file_mask, ignore_unknown, scope, exclude_by_ea, delta_signature, delta_sig_min_size, delta_mask, delta_diff, zeroing_neg_date, ignored_symlinks, mod_data_detect, sig_block_len); // build_delta_sig is not used for archive creation it is always implied when delta_signature is set } catch(...) { if(void_cat != nullptr) { delete void_cat; void_cat = nullptr; } throw; } if(void_cat != nullptr) { delete void_cat; void_cat = nullptr; } break; case oper_merge: if(ref_cat2 == nullptr || ! ref_cat2->get_in_place(ref2_in_place)) if(ref_cat1->get_in_place(ref1_in_place)) cat->set_in_place(ref1_in_place); // keeping the in-place of ref1 else cat->clear_in_place(); else // ref_cat2 exists and has an in_place path stored in ref2_in_place if(ref_cat1->get_in_place(ref1_in_place)) if(ref1_in_place == ref2_in_place) cat->set_in_place(ref1_in_place); // both the same in_place else cat->clear_in_place(); // different in-place paths, merging without it else cat->set_in_place(ref2_in_place); // only ref2 has in_place path if(ref_cat1->get_in_place(ref1_in_place)) if(info_details) get_ui().message(gettext("Processing files for merging...")); filtre_merge(get_pointer(), selection, subtree, pdesc, *cat, ref_cat1, ref_cat2, info_details, display_treated, display_treated_only_dir, display_skipped, *st_ptr, empty_dir, ea_mask, compr_mask, min_compr_size, keep_compressed, overwrite, warn_over, decremental, sparse_file_min_size, scope, delta_signature, build_delta_sig, delta_sig_min_size, delta_mask, sig_block_len); break; case oper_repair: if(ref_cat2 != nullptr) throw SRC_BUG; if(ref_cat1 == nullptr) throw SRC_BUG; if(ref_cat1->get_in_place(ref1_in_place)) cat->set_in_place(ref1_in_place); else cat->clear_in_place(); if(info_details) get_ui().message(gettext("Processing files for fixing...")); try { filtre_merge_step0(get_pointer(), ref_cat1, ref_cat2, *st_ptr, false, rep_decr, rep_over, aborting, thr_cancel); if(rep_decr != nullptr) throw SRC_BUG; // we should be prepared to release decr // but we do not need such argument for fixing op. filtre_merge_step2(get_pointer(), pdesc, *ref_cat1, info_details, display_treated, display_treated_only_dir, compr_mask, min_compr_size, keep_compressed, sparse_file_min_size, delta_signature, build_delta_sig, delta_sig_min_size, delta_mask, aborting, thr_cancel, true, delta_sig_block_size()); // we will not recalculate delta signature upon repairing // at this step, cat (the current archive's catalogue) is still empty // we will need to add ref_cat1's content at the end of the archive // not our own's content // changing the data_name of the catalogue that will be dropped at the // end of the archive to match the data_name of the slice header ref_cat1->set_data_name(internal_name); } catch(Erange & e) { // changing the data_name of the catalogue that will be dropped at the // end of the archive to match the data_name of the slice header ref_cat1->set_data_name(internal_name); } break; default: throw SRC_BUG; } thr_cancel.block_delayed_cancellation(true); // we must protect the following code against delayed cancellations } catch(Ethread_cancel & e) { disable_natural_destruction(); if(e.immediate_cancel()) throw; else { Ethread_cancel_with_attr *e_attr = dynamic_cast(&e); aborting = true; flag = e.get_flag(); if(e_attr != nullptr) aborting_next_etoile = e_attr->get_attr(); else aborting_next_etoile = 0; thr_cancel.block_delayed_cancellation(true); // we must protect the following code against delayed cancellations stack.top()->sync_write(); // flushing only the top of the stack (compressor) must not yet flush the below encryption layer!!! } } if(ref_cat1 != nullptr && op == oper_create) { if(info_details) get_ui().message(gettext("Adding reference to files that have been destroyed since reference backup...")); if(aborting) cat->update_absent_with(*ref_cat1, aborting_next_etoile); else st_ptr->add_to_deleted(cat->update_destroyed_with(*ref_cat1)); } cat->drop_escape_layer(); // we need to remove pointer to layers // that are about to be destroyed now macro_tools_close_layers(get_pointer(), stack, ver, op != oper_repair ? *cat : *ref_cat1, info_details, crypto, algo, gnupg_recipients, gnupg_signatories, empty); thr_cancel.block_delayed_cancellation(false); // release pending delayed cancellation (if any) if(aborting) throw Ethread_cancel(false, flag); } catch(...) { if(tmp != nullptr) { delete tmp; tmp = nullptr; } if(cat != nullptr) { delete cat; cat = nullptr; } throw; } } catch(Euser_abort & e) { disable_natural_destruction(); throw; } catch(Ebug & e) { throw; } catch(Erange &e) { string msg = string(gettext("Error while saving data: ")) + e.get_message(); throw Edata(msg); } } void archive::i_archive::free_mem() { stack.clear(); gnupg_signed.clear(); slices.clear(); ver.clear_crypted_key(); if(cat != nullptr) { delete cat; cat = nullptr; } } void archive::i_archive::check_gnupg_signed() const { list::const_iterator it = gnupg_signed.begin(); while(it != gnupg_signed.end() && it->result == signator::good) ++it; if(it != gnupg_signed.end()) get_ui().pause(gettext("WARNING! Incorrect signature found for archive, continue anyway?")); } void archive::i_archive::disable_natural_destruction() { sar *tmp = nullptr; trivial_sar *triv_tmp = nullptr; stack.find_first_from_bottom(tmp); if(tmp != nullptr) tmp->disable_natural_destruction(); else { stack.find_first_from_bottom(triv_tmp); if(triv_tmp != nullptr) triv_tmp->disable_natural_destruction(); } } void archive::i_archive::enable_natural_destruction() { sar *tmp = nullptr; trivial_sar *triv_tmp = nullptr; stack.find_first_from_bottom(tmp); if(tmp != nullptr) tmp->enable_natural_destruction(); else { stack.find_first_from_bottom(triv_tmp); if(triv_tmp != nullptr) triv_tmp->disable_natural_destruction(); } } const label & archive::i_archive::get_layer1_data_name() const { contextual *l1 = nullptr; stack.find_first_from_bottom(l1); if(l1 != nullptr) return l1->get_data_name(); else throw Erange("catalogue::get_data_name", gettext("Cannot get data name of the archive, this archive is not completely initialized")); } const label & archive::i_archive::get_catalogue_data_name() const { if(cat != nullptr) return cat->get_data_name(); else throw SRC_BUG; } bool archive::i_archive::only_contains_an_isolated_catalogue() const { return get_layer1_data_name() != get_catalogue_data_name() && ver.get_edition() >= 8; } void archive::i_archive::check_against_isolation(bool lax) const { if(cat != nullptr) { try { if(only_contains_an_isolated_catalogue()) { if(!lax) throw Erange("archive::i_archive::check_against_isolation", gettext("This archive contains an isolated catalogue, it cannot be used for this operation. It can only be used as reference for a incremental/differential backup or as backup of the original archive's catalogue")); // note1: that old archives do not have any data_name neither in the catalogue nor in the layer1 of the archive // both are faked equal to a zeroed label when reading them with recent dar version. Older archives than "08" would // thus pass this test successfully if no check was done against the archive version // note2: Old isolated catalogue do not carry any data, this is safe to try to restore them because any // pointer to data and/or EA has been removed during the isolation. else get_ui().pause(gettext("LAX MODE: Archive seems to be only an isolated catalogue (no data in it), Can I assume data corruption occurred and consider the archive as being a real archive?")); } } catch(Erange & e) { throw Erange("archive::i_archive::check_against_isolation", string(gettext("Error while fetching archive properties: ")) + e.get_message()); } } else throw SRC_BUG; // this method should be called once the archive object has been constructed // and this object should be totally exploitable, thus have an available catalogue } bool archive::i_archive::get_sar_param(infinint & sub_file_size, infinint & first_file_size, infinint & last_file_size, infinint & total_file_number) { sar *real_decoupe = nullptr; stack.find_first_from_bottom(real_decoupe); if(real_decoupe != nullptr) { slice_layout tmp = real_decoupe->get_slicing(); sub_file_size = tmp.other_size; first_file_size = tmp.first_size; if(real_decoupe->get_total_file_number(total_file_number) && real_decoupe->get_last_file_size(last_file_size)) return true; else // could not read size parameters throw Erange("archive::i_archive::get_sar_param", gettext("Sorry, file size is unknown at this step of the program.")); } else return false; } shared_ptr archive::i_archive::get_entrepot() { shared_ptr ret; sar *real_decoupe = nullptr; stack.find_first_from_bottom(real_decoupe); if(real_decoupe != nullptr) { ret = real_decoupe->get_entrepot(); if(!ret) throw SRC_BUG; } return ret; } infinint archive::i_archive::get_level2_size() { generic_file *level1 = stack.get_by_label(LIBDAR_STACK_LABEL_LEVEL1); if(dynamic_cast(level1) == nullptr) { stack.skip_to_eof(); return stack.get_position(); } else return 0; } const cat_directory *archive::i_archive::get_dir_object(const string & dir) const { const cat_directory *parent = nullptr; const cat_nomme *tmp_ptr = nullptr; parent = get_cat().get_contenu(); if(parent == nullptr) throw SRC_BUG; if(dir != "") { path search = dir; string tmp; bool loop = true; while(loop) { loop = search.pop_front(tmp); if(!loop) // failed because now, search is a one level path tmp = search.basename(); if(parent->search_children(tmp, tmp_ptr)) parent = dynamic_cast(tmp_ptr); else parent = nullptr; if(parent == nullptr) throw Erange("archive::i_archive::get_children_in_table", tools_printf("%S entry does not exist", &dir)); } } // else returning the root of the archive return parent; } static bool local_check_dirty_seq(escape *ptr) { bool ret; if(ptr != nullptr) { bool already_set = ptr->is_unjumpable_mark(escape::seqt_file); if(!already_set) ptr->add_unjumpable_mark(escape::seqt_file); ret = ptr != nullptr && ptr->skip_to_next_mark(escape::seqt_dirty, true); if(!already_set) ptr->remove_unjumpable_mark(escape::seqt_file); } else ret = false; return ret; } static void check_libgcrypt_hash_bug(user_interaction & dialog, hash_algo hash, const infinint & first_file_size, const infinint & file_size) { #if CRYPTO_AVAILABLE if(hash != hash_algo::none && !crypto_min_ver_libgcrypt_no_bug()) { const infinint limit = tools_get_extended_size("256G", 1024); if(file_size >= limit || first_file_size >= limit) dialog.pause(tools_printf(gettext("libgcrypt version < %s. Ligcrypt used has a bug that leads md5 and sha1 hash results to be erroneous for files larger than 256 Gio (gibioctet), do you really want to spend CPU cycles calculating a useless hash?"), MIN_VERSION_GCRYPT_HASH_BUG)); } #endif } } // end of namespace dar-2.7.15/src/libdar/mycurl_easyhandle_node.cpp0000644000175000017500000002535614636067146016572 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "mycurl_easyhandle_node.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "secu_string.hpp" #include "thread_cancellation.hpp" using namespace std; namespace libdar { #if LIBCURL_AVAILABLE /// helper function to handle libcurl error code /// wait or throw an exception depending on error condition /// \param[in] dialog used to report the reason we are waiting for and how much time we wait /// \param[in] err is the curl easy code to examin /// \param[in] wait_seconds is the time to wait for recoverable error /// \param[in] err_context is the error context message use to prepend waiting message or exception throw static void fichier_libcurl_check_wait_or_throw(const std::shared_ptr & dialog, CURLcode err, U_I wait_seconds, const std::string & err_context); bool mycurl_easyhandle_node::defaults_initialized = false; mycurl_param_list mycurl_easyhandle_node::defaults; constexpr const mycurl_easyhandle_node::opt_asso mycurl_easyhandle_node::association[]; mycurl_easyhandle_node::mycurl_easyhandle_node(const mycurl_easyhandle_node & ref) { // we avoid curl_easy_duphandle to postpone the operation // to the time apply() will be called init(); wanted = ref.current; (void) wanted.update_with(ref.wanted); } mycurl_easyhandle_node::mycurl_easyhandle_node(mycurl_easyhandle_node && ref) noexcept { handle = ref.handle; ref.handle = nullptr; current = move(ref.current); wanted = move(ref.wanted); } mycurl_easyhandle_node & mycurl_easyhandle_node::operator = (const mycurl_easyhandle_node & ref) { // handle is kept as is wanted = ref.current; (void) wanted.update_with(ref.wanted); return *this; } mycurl_easyhandle_node & mycurl_easyhandle_node::operator = (mycurl_easyhandle_node && ref) noexcept { // this->handle is kept as is wanted = std::move(ref.current); (void) wanted.update_with(ref.wanted); return *this; } void mycurl_easyhandle_node::setopt_default(CURLoption opt) { switch(get_opt_type(opt)) { case type_string: set_to_default(opt); break; case type_secu_string: set_to_default(opt); break; case type_pointer: set_to_default(opt); break; case type_long: set_to_default(opt); break; case type_mycurl_slist: set_to_default(opt); break; case type_curl_off_t: set_to_default(opt); break; case eolist: throw SRC_BUG; default: throw SRC_BUG; } } void mycurl_easyhandle_node::setopt_all_default() { const opt_asso *asso = association; while(asso != nullptr && asso->cast != eolist) { setopt_default(asso->opt); ++asso; } } void mycurl_easyhandle_node::apply(const std::shared_ptr & dialog, U_I wait_seconds, const bool & end_anyway) { list changed = current.update_with(wanted); list::iterator it = changed.begin(); CURLcode err = CURLE_OK; const string* t_string = nullptr; const secu_string *t_secu_string = nullptr; void * const * t_pointer = nullptr; const long* t_long = nullptr; const mycurl_slist* t_mycurl_slist = nullptr; const curl_off_t* t_curl_off_t = nullptr; thread_cancellation th; while(it != changed.end()) { switch(get_opt_type(*it)) { case type_string: if(!current.get_val(*it, t_string) || t_string == nullptr) throw SRC_BUG; err = curl_easy_setopt(handle, *it, t_string->empty() ? nullptr : t_string->c_str()); if(err != CURLE_OK) throw Erange("mycurl_easyhandle_node::apply", tools_printf(gettext("Error met while setting string option %d on libcurl handle: %s"), *it, curl_easy_strerror(err))); break; case type_secu_string: if(!current.get_val(*it, t_secu_string) || t_secu_string == nullptr) throw SRC_BUG; err = curl_easy_setopt(handle, *it, t_secu_string->empty() ? nullptr : t_secu_string->c_str()); if(err != CURLE_OK) throw Erange("mycurl_easyhandle_node::apply", tools_printf(gettext("Error met while setting secu_string option %d on libcurl handle: %s"), *it, curl_easy_strerror(err))); break; case type_pointer: if(!current.get_val(*it, t_pointer) || t_pointer == nullptr) throw SRC_BUG; err = curl_easy_setopt(handle, *it, *t_pointer); if(err != CURLE_OK) throw Erange("mycurl_easyhandle_node::apply", tools_printf(gettext("Error met while setting pointer option %d on libcurl handle: %s"), *it, curl_easy_strerror(err))); break; case type_long: if(!current.get_val(*it, t_long) || t_long == nullptr) throw SRC_BUG; err = curl_easy_setopt(handle, *it, *t_long); if(err != CURLE_OK) throw Erange("mycurl_easyhandle_node::apply", tools_printf(gettext("Error met while setting long option %d on libcurl handle: %s"), *it, curl_easy_strerror(err))); break; case type_mycurl_slist: if(!current.get_val(*it, t_mycurl_slist) || t_mycurl_slist == nullptr) throw SRC_BUG; err = curl_easy_setopt(handle, *it, t_mycurl_slist->empty() ? nullptr: t_mycurl_slist->get_address()); if(err != CURLE_OK) throw Erange("mycurl_easyhandle_node::apply", tools_printf(gettext("Error met while setting curl_slist option %d on libcurl handle: %s"), *it, curl_easy_strerror(err))); break; case type_curl_off_t: if(!current.get_val(*it, t_curl_off_t) || t_curl_off_t == nullptr) throw SRC_BUG; err = curl_easy_setopt(handle, *it, *t_curl_off_t); if(err != CURLE_OK) throw Erange("mycurl_easyhandle_node::apply", tools_printf(gettext("Error met while setting curl_off_t option %d on libcurl handle: %s"), *it, curl_easy_strerror(err))); break; case eolist: throw SRC_BUG; default: throw SRC_BUG; } ++it; } wanted.clear(); do { err = curl_easy_perform(handle); if(!end_anyway) { fichier_libcurl_check_wait_or_throw(dialog, err, wait_seconds, "Error met while performing action on libcurl handle"); th.check_self_cancellation(); } } while(err != CURLE_OK && !end_anyway); } void mycurl_easyhandle_node::init() { handle = curl_easy_init(); if(handle == nullptr) throw Erange("mycurl_easyhandle_node::mycurl_easyhandle_node", gettext("Error met while creating a libcurl handle")); } void mycurl_easyhandle_node::init_defaults() { if(!defaults_initialized) { defaults_initialized = true; string arg_string(""); secu_string arg_secu_string; void *arg_ptr = nullptr; long arg_long = 0; mycurl_slist arg_mycurl_slist; curl_off_t arg_curl_off_t = 0; const opt_asso *cursor = association; while(cursor->cast != eolist) { switch(cursor->cast) { case type_string: defaults.add(cursor->opt, arg_string); // a string (thus char*), a nullptr else break; case type_secu_string: defaults.add(cursor->opt, arg_secu_string); break; case type_pointer: defaults.add(cursor->opt, arg_ptr); break; case type_long: defaults.add(cursor->opt, arg_long); break; case type_mycurl_slist: defaults.add(cursor->opt, arg_mycurl_slist); break; case type_curl_off_t: defaults.add(cursor->opt, arg_curl_off_t); break; case eolist: throw SRC_BUG; default: throw SRC_BUG; } ++cursor; } } } mycurl_easyhandle_node::opttype mycurl_easyhandle_node::get_opt_type(CURLoption opt) { const opt_asso *ptr = association; while(ptr->cast != eolist && ptr->opt != opt) ++ptr; if(ptr->cast != eolist) return ptr->cast; else throw SRC_BUG; // unknown option type } static void fichier_libcurl_check_wait_or_throw(const shared_ptr & dialog, CURLcode err, U_I wait_seconds, const string & err_context) { switch(err) { case CURLE_OK: case CURLE_BAD_DOWNLOAD_RESUME: break; case CURLE_REMOTE_DISK_FULL: case CURLE_UPLOAD_FAILED: throw Edata(curl_easy_strerror(err)); case CURLE_FTP_ACCEPT_FAILED: case CURLE_UNSUPPORTED_PROTOCOL: case CURLE_FAILED_INIT: case CURLE_URL_MALFORMAT: case CURLE_NOT_BUILT_IN: case CURLE_WRITE_ERROR: case CURLE_READ_ERROR: case CURLE_OUT_OF_MEMORY: case CURLE_RANGE_ERROR: case CURLE_FILE_COULDNT_READ_FILE: case CURLE_FUNCTION_NOT_FOUND: case CURLE_ABORTED_BY_CALLBACK: case CURLE_BAD_FUNCTION_ARGUMENT: case CURLE_TOO_MANY_REDIRECTS: case CURLE_UNKNOWN_OPTION: case CURLE_FILESIZE_EXCEEDED: case CURLE_REMOTE_FILE_EXISTS: case CURLE_REMOTE_FILE_NOT_FOUND: case CURLE_PARTIAL_FILE: case CURLE_QUOTE_ERROR: throw Erange("entrepot_libcurl::check_wait_or_throw", tools_printf(gettext("%S: %s, aborting"), &err_context, curl_easy_strerror(err))); case CURLE_LOGIN_DENIED: case CURLE_REMOTE_ACCESS_DENIED: case CURLE_PEER_FAILED_VERIFICATION: throw Enet_auth(tools_printf(gettext("%S: %s, aborting"), &err_context, curl_easy_strerror(err))); case CURLE_COULDNT_RESOLVE_PROXY: case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_CONNECT: case CURLE_FTP_ACCEPT_TIMEOUT: case CURLE_FTP_CANT_GET_HOST: case CURLE_OPERATION_TIMEDOUT: case CURLE_SEND_ERROR: case CURLE_RECV_ERROR: case CURLE_AGAIN: default: if(wait_seconds > 0) { dialog->printf(gettext("%S: %s, retrying in %d seconds"), &err_context, curl_easy_strerror(err), wait_seconds); sleep(wait_seconds); } else dialog->pause(tools_printf(gettext("%S: %s, do we retry network operation?"), &err_context, curl_easy_strerror(err))); break; } } #endif } // end of namespace dar-2.7.15/src/libdar/cat_mirage.hpp0000644000175000017500000001300014636066467014141 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_mirage.hpp /// \brief smart pointer to an etoile object. Used to store hard link information inside a catalogue /// \ingroup Private #ifndef CAT_MIRAGE_HPP #define CAT_MIRAGE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_nomme.hpp" #include "cat_etoile.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the hard link implementation, cat_mirage is the named entry owned by a directory it points to a common "cat_etoile class" /// well, a mirage is this fake apparition of water in a desert... I guess you get the picture now... :-) class cat_mirage : public cat_nomme { public: /// format of mirage enum mirage_format {fmt_mirage, ///< new format fmt_hard_link, ///< old dual format fmt_file_etiquette }; ///< old dual format cat_mirage(const std::string & name, cat_etoile *ref): cat_nomme(name, saved_status::saved) { dup_on(ref); }; cat_mirage(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, entree_stats & stats, std::map & corres, compression default_algo, mirage_format fmt, bool lax, bool small); cat_mirage(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, entree_stats & stats, std::map & corres, compression default_algo, bool lax, bool small); cat_mirage(const cat_mirage & ref) : cat_nomme (ref) { dup_on(ref.star_ref); }; cat_mirage(cat_mirage && ref) noexcept: cat_nomme(std::move(ref)) { try { dup_on(ref.star_ref); } catch(...) {}; }; cat_mirage & operator = (const cat_mirage & ref); cat_mirage & operator = (cat_mirage && ref); ~cat_mirage() { star_ref->drop_ref(this); }; virtual bool operator == (const cat_entree & ref) const override; virtual unsigned char signature() const override { return 'm'; }; virtual std::string get_description() const override { return "hard linked inode"; }; virtual cat_entree *clone() const override { return new (std::nothrow) cat_mirage(*this); }; cat_inode *get_inode() const { if(star_ref == nullptr) throw SRC_BUG; return star_ref->get_inode(); }; infinint get_etiquette() const { return star_ref->get_etiquette(); }; infinint get_etoile_ref_count() const { return star_ref->get_ref_count(); }; cat_etoile *get_etoile() const { return star_ref; }; bool is_inode_counted() const { return star_ref->is_counted(); }; bool is_inode_wrote() const { return star_ref->is_wrote(); }; bool is_inode_dumped() const { return star_ref->is_dumped(); }; void set_inode_counted(bool val) const { star_ref->set_counted(val); }; void set_inode_wrote(bool val) const { star_ref->set_wrote(val); }; void set_inode_dumped(bool val) const { star_ref->set_dumped(val); }; virtual void post_constructor(const pile_descriptor & pdesc) override; /// whether we are the mirage that triggered this hard link creation bool is_first_mirage() const { return star_ref->get_first_ref() == this; }; // overwriting virtual method from cat_entree virtual void change_location(const smart_pointer & pdesc) override { get_inode()->change_location(pdesc); }; /// always write the inode as a hardlinked inode /// \note when calling dump() on a mirage by default if the inode pointed /// by the mirage has is referred only once, it is saved as a normal inode /// that's to say a non hard linked inode. In some circumstances, the total /// number of hard link on that inode is not yet known at the time the inode /// is written (repair operation), we cannot assume the hard linked inode is /// a normal inode as new hard links pointing on that same inode may have not /// been read yet. void disable_reduction_to_normal_inode() { star_ref->disable_reduction_to_normal_inode(); }; protected: virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; private: cat_etoile *star_ref; void init(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, entree_stats & stats, std::map & corres, compression default_algo, mirage_format fmt, bool lax, bool small); void dup_on(cat_etoile * ref); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/crypto.cpp0000644000175000017500000000557214636066467013400 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "crypto.hpp" #include "erreurs.hpp" using namespace std; namespace libdar { string crypto_algo_2_string(crypto_algo algo) { switch(algo) { case crypto_algo::none: return gettext("none"); case crypto_algo::scrambling: return gettext("scrambling (weak encryption)"); case crypto_algo::blowfish: return "blowfish"; case crypto_algo::aes256: return "AES 256"; case crypto_algo::twofish256: return "twofish 256"; case crypto_algo::serpent256: return "serpent 256"; case crypto_algo::camellia256: return "camellia 256"; default: throw SRC_BUG; } } char crypto_algo_2_char(crypto_algo a) { switch(a) { case crypto_algo::none: return 'n'; case crypto_algo::scrambling: return 's'; case crypto_algo::blowfish: return 'b'; case crypto_algo::aes256: return 'a'; case crypto_algo::twofish256: return 't'; case crypto_algo::serpent256: return 'p'; case crypto_algo::camellia256: return 'c'; default: throw SRC_BUG; } } crypto_algo char_2_crypto_algo(char a) { switch(a) { case 'n': return crypto_algo::none; case 's': return crypto_algo::scrambling; case 'b': return crypto_algo::blowfish; case 'a': return crypto_algo::aes256; case 't': return crypto_algo::twofish256; case 'p': return crypto_algo::serpent256; case 'c': return crypto_algo::camellia256; default: throw Erange("char_to_sym_crypto", gettext("Unknown crypto algorithm")); } } bool same_signatories(const std::list & a, const std::list & b) { list::const_iterator ita = a.begin(); list::const_iterator itb = b.begin(); while(ita != a.end() && itb != b.end() && *ita == *itb) { ++ita; ++itb; } return (ita == a.end() && itb == b.end()); } } // end of namespace dar-2.7.15/src/libdar/parallel_tronconneuse.cpp0000644000175000017500000013524714636067146016454 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "parallel_tronconneuse.hpp" #include "tools.hpp" using namespace std; using namespace libthreadar; namespace libdar { ///////////////////////////////////////////////////// // // static routines declaration // // static void remove_trailing_clear_data_from_encrypted_buf(const infinint & read_offset, ///< offset of the first byte of the 'first' segment const archive_version & reading_ver, ///< read archive format version const infinint & initial_shift, ///< amount of free bytes before encrypted ones infinint (*callback)(generic_file & below, const archive_version& reading_version), unique_ptr & first, ///< pointer to a existing segment unique_ptr & opt_next, ///< in option pointer to the segment following the first in the crypted data bool & reof); ///////////////////////////////////////////////////// // // parallel_tronconneuse implementation // // parallel_tronconneuse::parallel_tronconneuse(U_I workers, U_32 block_size, generic_file & encrypted_side, const archive_version & ver, std::unique_ptr & crypto_ptr): proto_tronco(encrypted_side.get_mode() == gf_read_only ? gf_read_only : gf_write_only) { if(block_size == 0) throw Erange("parallel_tronconneuse::parallel_tronconneuse", tools_printf(gettext("%d is not a valid block size"), block_size)); num_workers = workers; clear_block_size = block_size; current_position = 0; initial_shift = 0; reading_ver = ver; crypto = move(crypto_ptr); t_status = thread_status::dead; ignore_stop_acks = 0; mycallback = nullptr; encrypted = &encrypted_side; // used for further reference, thus the encrypted object must survive "this" lus_data.clear(); lus_flags.clear(); lus_eof = false; check_bytes_to_skip = true; block_num = 0; if(!crypto) throw SRC_BUG; // creating the inter-thread communication structures try { U_I tmp_bs1, tmp_bs2; scatter.reset(new (nothrow) ratelier_scatter(get_ratelier_size(num_workers))); if(!scatter) throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); gather.reset(new (nothrow) ratelier_gather(get_ratelier_size(num_workers))); if(!gather) throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); waiter.reset(new (nothrow) barrier(num_workers + 2)); if(!waiter) throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); // tas is created empty tas.reset(new (nothrow) heap()); if(!tas) throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); // filling the heap (tas) with preallocated crypto_segments tmp_bs1 = crypto->encrypted_block_size_for(clear_block_size); tmp_bs2 = crypto->clear_block_allocated_size_for(clear_block_size); for(U_I i = 0; i < get_heap_size(num_workers); ++i) tas->put(make_unique(tmp_bs1,tmp_bs2)); // creating and running the sub-thread objects for(U_I i = 0; i < workers; ++i) travailleur.push_back(make_unique(scatter, gather, waiter, crypto->clone(), get_mode() == gf_write_only) ); switch(get_mode()) { case gf_read_only: crypto_reader = make_unique(scatter, waiter, num_workers, clear_block_size, encrypted, tas, initial_shift); if(!crypto_reader) throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); break; case gf_write_only: crypto_writer = make_unique(gather, waiter, num_workers, encrypted, tas); if(!crypto_writer) throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } run_threads(); // threads starts in a suspended state (pending on the waiter barrier) } catch(std::bad_alloc &) { throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); } } parallel_tronconneuse::~parallel_tronconneuse() noexcept { try { terminate(); } catch(...) { // not throwing exception from destructor } } bool parallel_tronconneuse::skippable(skippability direction, const infinint & amount) { if(get_mode() != gf_read_only) return false; send_read_order(tronco_flags::stop); return encrypted->skippable(direction, amount); } bool parallel_tronconneuse::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; if(get_mode() != gf_read_only) throw SRC_BUG; if(pos == current_position) return true; // looking in lus_data for the offset // before sending the stop order if(!find_offset_in_lus_data(pos)) { if(ignore_stop_acks == 0) { if(send_read_order(tronco_flags::stop, pos)) { // the stop order completed and // threads are now stopped current_position = pos; lus_eof = false; } // else we are in the same situation as // if the offset was found in lus_data // but a stop ack order are pending in // the ratelier_gather, for that reason // ignore_stop_acks field has been set the number // workers which is the number of expected pending stop orders // by send_read_order() to ignore these acks // during further readings // current_position has been set by send_read_order() (calling find_offset_in_lus_data()) } else // some unacknowledged stop order are pending in the pipe, we can read further up to them, subthreads are *maybe* already suspended { // we do not need to send a stop order if(purge_unack_stop_order(pos)) { // pending stop found or eof found current_position = pos; lus_eof = false; } // current_position has been set by purge_unack_stop_order (calling find_offset_in_lus_data()) } } // else current_position has been set by find_offset_in_lus_data // offset has been found in lus_data, no stop order needed to be sent to skip() return true; } bool parallel_tronconneuse::skip_to_eof() { bool ret; if(is_terminated()) throw SRC_BUG; if(get_mode() != gf_read_only) throw SRC_BUG; send_read_order(tronco_flags::stop); ret = encrypted->skip_to_eof(); if(ret) { infinint residu; infinint block_num; U_32 encrypted_buf_size = crypto->encrypted_block_size_for(clear_block_size); unique_ptr aux = tas->get(); try { if(encrypted->get_position() < initial_shift) throw SRC_BUG; // eof is before the first encrypted byte euclide(encrypted->get_position() - initial_shift, encrypted_buf_size, block_num, residu); current_position = block_num * infinint(clear_block_size); if(residu > 0) { go_read(); // we will read the last block and uncipher it to know the exact // position of eof from clear side point of view while(read(aux->clear_data.get_addr(), aux->clear_data.get_max_size()) == aux->clear_data.get_max_size()) ; // nothing in the while loop } } catch(...) { tas->put(move(aux)); throw; } tas->put(move(aux)); } return ret; } bool parallel_tronconneuse::skip_relative(S_I x) { bool ret; if(is_terminated()) throw SRC_BUG; if(get_mode() != gf_read_only) throw SRC_BUG; if(x >= 0) ret = skip(current_position + x); else { x = -x; if(current_position >= x) ret = skip(current_position - infinint(x)); else { skip(0); ret = false; } } return ret; } void parallel_tronconneuse::set_initial_shift(const infinint & x) { if(is_terminated()) throw SRC_BUG; initial_shift = x; if(get_mode() == gf_read_only) { send_read_order(tronco_flags::stop); crypto_reader->set_initial_shift(x); } } void parallel_tronconneuse::set_callback_trailing_clear_data(trailing_clear_data_callback call_back) { if(crypto_reader) { mycallback = call_back; crypto_reader->set_callback_trailing_clear_data(call_back); } else throw SRC_BUG; } void parallel_tronconneuse::inherited_read_ahead(const infinint & amount) { if(get_mode() != gf_read_only) throw SRC_BUG; // nothing special to do, the below thread // will read as much as possible until it // reaches eof or is interrupted by the // master thread if(is_terminated()) throw SRC_BUG; else go_read(); } U_I parallel_tronconneuse::inherited_read(char *a, U_I size) { U_I ret = 0; U_I added_to_current_pos = 0; if(get_mode() != gf_read_only) throw SRC_BUG; if(lus_eof) return ret; go_read(); while(ret < size && ! lus_eof) { read_refill(); switch(static_cast(lus_flags.front())) { case tronco_flags::normal: if(lus_data.empty()) throw SRC_BUG; // not the same amount of item in lus_data than in lus_flags! if(!lus_data.front()) throw SRC_BUG; // front pointer does not point to any crypto_segment object ret += lus_data.front()->clear_data.read(a + ret, size - ret); if(lus_data.front()->clear_data.all_is_read()) { tas->put(move(lus_data.front())); lus_data.pop_front(); lus_flags.pop_front(); } break; case tronco_flags::stop: if(ignore_stop_acks > 0) { --ignore_stop_acks; tas->put(move(lus_data.front())); lus_data.pop_front(); lus_flags.pop_front(); if(ignore_stop_acks == 0) { t_status = thread_status::suspended; current_position += (ret - added_to_current_pos); added_to_current_pos = ret; go_read(); } } else throw SRC_BUG; break; case tronco_flags::eof: lus_eof = true; if(purge_ratelier_from_next_order() != tronco_flags::eof) throw SRC_BUG; break; case tronco_flags::die: throw SRC_BUG; // should never receive a die flag without a first sollicition case tronco_flags::data_error: if(lus_data.empty()) throw SRC_BUG; // not the same amount of item in lus_data than in lus_flags! if(!lus_data.front()) throw SRC_BUG; // front does not point to any crypto_segment pointed object // we must check that the deciphering error // is not due to the fact some clear data (archive terminator) // has been mixed with the encrypted data and tried // to be decrypted if(mycallback != nullptr) { unique_ptr current(move(lus_data.front())); if(!current) throw SRC_BUG; try { infinint crypt_offset = current->block_index * crypto->encrypted_block_size_for(clear_block_size) + initial_shift; // now fetching the next segment following 'current' if any // in order to append it to current and remove trailing clear data at the tail lus_data.pop_front(); lus_flags.pop_front(); read_refill(); // this is be sure we will have the next available segment try { if(lus_flags.size() > 0 && (lus_flags.front() == static_cast(tronco_flags::normal) || lus_flags.front() == static_cast(tronco_flags::data_error) ) ) { if(lus_data.empty()) throw SRC_BUG; // an element should be present, as lus_flags is not empty unique_ptr & opt = lus_data.front(); // this is a reference on an unique_ptr // the unique_ptr pointed to by 'opt' stays managed by lus_data remove_trailing_clear_data_from_encrypted_buf(crypt_offset, reading_ver, initial_shift, mycallback, current, opt, lus_eof); } else // we ignore this next segment (eof reached at an exact segment size boundary) { unique_ptr opt = nullptr; remove_trailing_clear_data_from_encrypted_buf(crypt_offset, reading_ver, initial_shift, mycallback, current, opt, lus_eof); } } catch(Erange & e) { // we must change this exception message // to something more relevant to the context throw Erange("parallel_tronconneuse::inherited_read", gettext("data deciphering failed")); } // now current should have possible clear data removed // from the tail and only contain encrypted data // retrying the deciphering that failed in the worker: current->clear_data.set_data_size(crypto->decrypt_data(current->block_index, current->crypted_data.get_addr(), current->crypted_data.get_data_size(), current->clear_data.get_addr(), current->clear_data.get_max_size())); current->clear_data.rewind_read(); } catch(...) { tas->put(move(current)); throw; } // we reached this statement means no exception took place // thus deciphering succeeded and we can reinsert the 'current' // crypto_segment at the front of lus_data with a normal flag: lus_data.push_front((move(current))); lus_flags.push_front(static_cast(tronco_flags::normal)); } else // mycallback == nullptr { // without callback we cannot remove possible clear data at the tail // so we try to reproduce the exception met by the worker for it // propagates to our caller: unique_ptr & current = lus_data.front(); // 'current' is here a reference to an unique_ptr // and stays managed by lus_data // this should throw the exception met by the worker current->clear_data.set_data_size(crypto->decrypt_data(current->block_index, current->crypted_data.get_addr(), current->crypted_data.get_data_size(), current->clear_data.get_addr(), current->clear_data.get_max_size())); // if no exception arose, we probably hit a bug throw SRC_BUG; } break; case tronco_flags::exception_below: join_threads(); throw SRC_BUG; // the previous call should throw an exception if not this is a bug case tronco_flags::exception_worker: // a worker has a problem, the other as well as the below thread are not // aware of that, for that reason the faulty worker is still alive just // waiting for the order to die, which purge_ratelier will do on purpose // when cleaning up this exception_worker block and throw the worker exception (void)purge_ratelier_from_next_order(); throw SRC_BUG; // else this is a bug condition default: throw SRC_BUG; } } current_position += (ret - added_to_current_pos); return ret; } void parallel_tronconneuse::inherited_write(const char *a, U_I size) { U_I wrote = 0; U_I remain; if(get_mode() != gf_write_only) throw SRC_BUG; if(t_status == thread_status::dead) run_threads(); while(wrote < size) { if(crypto_writer->exception_pending()) { try { stop_threads(); join_threads(); throw SRC_BUG; // if join did not through an exception } catch(...) { // we have to reset the object position // to where the error took place block_num = crypto_writer->get_error_block(); current_position = block_num*clear_block_size; // truncate below if possible try { encrypted->truncate(initial_shift + block_num*crypto->encrypted_block_size_for(clear_block_size)); } catch(...) { encrypted->skip(initial_shift + block_num*crypto->encrypted_block_size_for(clear_block_size)); } throw; } } if(!tempo_write) // no crypto_segment pointed to by tempo_write { tempo_write = tas->get(); tempo_write->reset(); tempo_write->block_index = block_num++; if(tempo_write->clear_data.get_max_size() < clear_block_size) throw SRC_BUG; } remain = size - wrote; if(remain + tempo_write->clear_data.get_data_size() > clear_block_size) remain = clear_block_size - tempo_write->clear_data.get_data_size(); wrote += tempo_write->clear_data.write(a + wrote, remain); if(tempo_write->clear_data.get_data_size() == clear_block_size) scatter->scatter(tempo_write, static_cast(tronco_flags::normal)); } current_position += wrote; } void parallel_tronconneuse::inherited_sync_write() { if(get_mode() == gf_write_only) { if(tempo_write) scatter->scatter(tempo_write, static_cast(tronco_flags::normal)); } } void parallel_tronconneuse::inherited_flush_read() { if(get_mode() == gf_read_only) send_read_order(tronco_flags::stop); } void parallel_tronconneuse::inherited_terminate() { if(get_mode() == gf_write_only) sync_write(); if(get_mode() == gf_read_only) flush_read(); switch(t_status) { case thread_status::running: case thread_status::suspended: stop_threads(); // stop threads first if relevant /* no break */ case thread_status::dead: join_threads(); // may throw exception break; default: throw SRC_BUG; } // sanity checks if(tas->get_size() != get_heap_size(num_workers)) throw SRC_BUG; } bool parallel_tronconneuse::send_read_order(tronco_flags order, const infinint & for_offset) { bool ret = true; tronco_flags val; if(get_mode() != gf_read_only) throw SRC_BUG; if(t_status == thread_status::dead) throw SRC_BUG; switch(order) { case tronco_flags::die: crypto_reader->set_flag(order); if(t_status == thread_status::suspended) { waiter->wait(); t_status = thread_status::running; } val = purge_ratelier_from_next_order(); switch(val) { case tronco_flags::die: // expected normal condition break; case tronco_flags::eof: // eof met threads are stopped /* no break */ case tronco_flags::stop: // pending stop was not yet read // now that threads are suspended we can // send the order again crypto_reader->set_flag(order); if(t_status == thread_status::suspended) { waiter->wait(); // awaking thread pending on waiter for they take the order into account t_status = thread_status::running; } val = purge_ratelier_from_next_order(); if(val != order) throw SRC_BUG; break; case tronco_flags::normal: throw SRC_BUG; // purge_ratelier_from_next_order() should have drop those case tronco_flags::data_error: throw SRC_BUG; // purge_ratelier_from_next_order() should have drop those case tronco_flags::exception_below: // unexpected but possible, has the same outcome as "die", so we are good break; case tronco_flags::exception_worker: throw SRC_BUG; // purge_ratelier_from_next_order() should handle that // an trigger the launch of worker exception default: throw SRC_BUG; } break; case tronco_flags::eof: throw SRC_BUG; case tronco_flags::stop: if(t_status == thread_status::suspended) return ret; // nothing to do crypto_reader->set_flag(order); val = purge_ratelier_from_next_order(for_offset); if(val != tronco_flags::stop && val != tronco_flags::eof && (val != tronco_flags::normal || for_offset.is_zero())) throw SRC_BUG; if(!for_offset.is_zero() && val == tronco_flags::normal) ret = false; // order not purged, not yet subthreaded will be considered suspended // after the order completion (ignore_stop_order back to zero) break; case tronco_flags::normal: throw SRC_BUG; case tronco_flags::data_error: throw SRC_BUG; case tronco_flags::exception_below: throw SRC_BUG; case tronco_flags::exception_worker: throw SRC_BUG; default: throw SRC_BUG; } return ret; } void parallel_tronconneuse::send_write_order(tronco_flags order) { if(t_status == thread_status::dead) throw SRC_BUG; switch(order) { case tronco_flags::normal: case tronco_flags::stop: case tronco_flags::eof: case tronco_flags::data_error: case tronco_flags::exception_below: case tronco_flags::exception_worker: throw SRC_BUG; case tronco_flags::die: break; default: throw SRC_BUG; } sync_write(); if(tempo_write) throw SRC_BUG; for(U_I i = 0; i < num_workers; ++i) { tempo_write = tas->get(); scatter->scatter(tempo_write, static_cast(order)); } if(order != tronco_flags::die) // OK OK, today only "die" is expected waiter->wait(); // to release threads once order has been received } void parallel_tronconneuse::go_read() { if(t_status == thread_status::dead) run_threads(); if(t_status == thread_status::suspended) { crypto_reader->set_pos(current_position); crypto_reader->set_flag(tronco_flags::normal); waiter->wait(); // this should release the workers and the crypto_reader thread t_status = thread_status::running; check_bytes_to_skip = true; } } void parallel_tronconneuse::read_refill() { if(lus_data.empty() && t_status != thread_status::dead) { if(!lus_flags.empty()) throw SRC_BUG; gather->gather(lus_data, lus_flags); if(lus_flags.empty() || lus_data.empty()) throw SRC_BUG; // show receive some blocks with either flag eof, error or data if(check_bytes_to_skip && static_cast(lus_flags.front()) == tronco_flags::normal) { infinint bytes_to_skip = crypto_reader->get_pos_in_flow(); check_bytes_to_skip = false; if(!bytes_to_skip.is_zero()) { U_I to_skip = 0; bytes_to_skip.unstack(to_skip); if(!bytes_to_skip.is_zero()) throw SRC_BUG; // should be castable to an U_I if(lus_data.front()->clear_data.get_data_size() >= to_skip) lus_data.front()->clear_data.rewind_read(to_skip); else throw SRC_BUG; // offset to skip should be within the segment switch(static_cast(lus_flags.front())) { case tronco_flags::normal: break; case tronco_flags::stop: throw SRC_BUG; case tronco_flags::eof: throw SRC_BUG; case tronco_flags::die: throw SRC_BUG; case tronco_flags::data_error: break; case tronco_flags::exception_below: throw SRC_BUG; case tronco_flags::exception_worker: throw SRC_BUG; default: throw SRC_BUG; } } } } } tronco_flags parallel_tronconneuse::purge_ratelier_from_next_order(infinint pos) { U_I num = travailleur.size(); // the number of worker deque::iterator it; tronco_flags ret = tronco_flags::normal; if(t_status == thread_status::dead) throw SRC_BUG; do { read_refill(); // only if no order block has been found, if pos is given if(!pos.is_zero() && ret == tronco_flags::normal) { if(find_offset_in_lus_data(pos)) { ignore_stop_acks = num; num = 0; // current method will return tronco_flags::normal // when reading further // data we will met the order acks we were // cleaning here, but doing that we've found // the data we were looking for too, so the // order has not been purged from the ratelier } } while(!lus_flags.empty() && num > 0) { switch(static_cast(lus_flags.front())) { case tronco_flags::stop: case tronco_flags::die: case tronco_flags::eof: case tronco_flags::exception_below: if(ret == tronco_flags::normal) // first order found { ret = static_cast(lus_flags.front()); if(ret != tronco_flags::stop && ret != tronco_flags::eof && ignore_stop_acks > 0) { ignore_stop_acks = 0; // this situation occurs when skip triggered // a stop order which was not purged because // the requested data was found in the pipe // from subthreads while the subthreads reached // eof and were thus suspended. In that case // the stop order did not triggered any ack // we just have to consider that the subthreads // are actually suspended (which status would // else have been set after the purge of the pending // stop orders) } } if(static_cast(lus_flags.front()) == ret) { if(ignore_stop_acks > 0) // only when ret is tronco_flags::stop or tronco_flags::eof we can have this condition // (ignore_stop_acks is reset to zero in the code just above for other cases) { --ignore_stop_acks; // first purging aborted stop acks if(ignore_stop_acks == 0) { // now that the last stop/eof pending acks // is about to be purged we can set the current thread status t_status = thread_status::suspended; if(ret != tronco_flags::eof) { go_read(); // yes we trigger the sub thread to push their data // to the ratelier up to the current order acknolegment ret = tronco_flags::normal; // getting ready to read the "first" order // now that all aborted stop order have been purged pos = 0; // but we must ignore the pos lookup // as this ending purge of stop order // may have dropped some interleaved data // (normal) blocks, thus the offset of // data from the pipe is now unknown } else // we are done num = 0; } } else { --num; if(num == 0) { if(ret == tronco_flags::die) t_status = thread_status::dead; else t_status = thread_status::suspended; if(ret == tronco_flags::exception_below) { t_status = thread_status::dead; // all threads should have ended // below thread ended with an exception join_threads(); // we thus relaunch the exception in the curren thread // else this is a bug: throw SRC_BUG; } } } } else throw SRC_BUG; // melted orders in the pipe! // the N order should follow each other before and // not be mixed with other orders break; case tronco_flags::exception_worker: // unlike other orders this one could be single in the pipe // we just have to drive the threads to die to // get the worker exception thrown in the current thread lus_flags.pop_front(); tas->put(move(lus_data.front())); lus_data.pop_front(); send_read_order(tronco_flags::die); // this leads to a recursive call !!! join_threads(); // this should propagate the worker exception throw SRC_BUG; // else this is a bug condition case tronco_flags::normal: // we get here only if a order block // has been found or if pos equals zero // and we drop any further data block now break; case tronco_flags::data_error: break; default: throw SRC_BUG; } lus_flags.pop_front(); tas->put(move(lus_data.front())); lus_data.pop_front(); } } while(num > 0); return ret; } bool parallel_tronconneuse::purge_unack_stop_order(const infinint & pos) { bool ret = pos.is_zero() || !find_offset_in_lus_data(pos); bool loop = ignore_stop_acks > 0 && ret; // we loop only if there is some pending acks to purge // and if either we do not have to look for a given offset // or we could not find the pos position in lus_data bool lookup = true; // if lookup becomes false, we ignore pos and the offset lookup if(t_status == thread_status::dead) throw SRC_BUG; while(loop) { read_refill(); while(!lus_flags.empty() && loop) { switch(static_cast(lus_flags.front())) { case tronco_flags::stop: case tronco_flags::eof: // eof can be found if(ignore_stop_acks == 0) throw SRC_BUG; else --ignore_stop_acks; if(ignore_stop_acks == 0) loop = false; // some data block could be present in lus_data // after this last stop block and could match // the expected offset, but we would have to retur // true as purge order completed but would // also to return false at the same time as the // expected offset has been found... so we // ignore any data past the last stop block // even if it is accessible and could match // our lookup break; case tronco_flags::die: throw SRC_BUG; case tronco_flags::normal: case tronco_flags::data_error: if(lookup && ! pos.is_zero() && find_offset_in_lus_data(pos)) { loop = false; ret = false; } else { // different fates car take place due to find_offset_in_lus_data() exec: // - the original data block is in place (requested offset before current) // - original block has been removed (lus_data eventually void) if // the requested offset if past all data block that could be found // in the pipe (but for an non data block is hit), same situation as if // find_offset_in_lus_data() has not been executed (pos == 0) // - the front() block in lus_data is now a non-data block if( ! lus_flags.empty() && static_cast(lus_flags.front()) != tronco_flags::data_error && static_cast(lus_flags.front()) != tronco_flags::normal) continue; // find_offset_in_lus_data has purged all the data entry before a non-data // one, we must not purge this block before inspecting it (code below) but // have to restart the while loop from the beginning (so we "continue") // ELSE we exit the switch construct (break) and execute the purge code // after it (leading data block has to be removed) else lookup = false; // the code aftet this switch construct will "manually" remove the // leading data block without current_offset update, // which will make the current offset info wrong // se must not more look for an offset (it has no use, // because find_offset_in_lus_data()) would have remove all data block // eventually hitting a non-data block if there was chance to find the // requested offset looking forward. } break; case tronco_flags::exception_below: join_threads(); throw SRC_BUG; case tronco_flags::exception_worker: purge_ratelier_from_next_order(); throw SRC_BUG; default: throw SRC_BUG; } if(loop || ret) { if(!lus_flags.empty()) { lus_flags.pop_front(); if(lus_data.empty()) throw SRC_BUG; tas->put(move(lus_data.front())); lus_data.pop_front(); } } } } if(ret) t_status = thread_status::suspended; return ret; } bool parallel_tronconneuse::find_offset_in_lus_data(const infinint & pos) { bool search = true; bool found = false; // checking first whether we do not already have the requested data while(search && !found) { if(lus_data.empty()) { search = false; continue; } if(lus_flags.empty()) throw SRC_BUG; if(static_cast(lus_flags.front()) != tronco_flags::normal) { search = false; continue; } if(pos < current_position) { infinint lu = lus_data.front()->clear_data.get_read_offset(); if(current_position <= pos + lu) { U_I lu_tmp = 0; lu -= current_position - pos; lu.unstack(lu_tmp); if(!lu.is_zero()) throw SRC_BUG; // we should be in the range of a U_I lus_data.front()->clear_data.rewind_read(lu_tmp); current_position = pos; found = true; } else search = false; // else we need to send_read_order() // requested offset is further before // the earliest data we have in lus_data } else // pos > current_position (the case pos == current_position has alread been seen) { infinint alire = lus_data.front()->clear_data.get_data_size() - lus_data.front()->clear_data.get_read_offset(); if(pos < current_position + alire) { U_I alire_tmp = 0; alire = infinint(lus_data.front()->clear_data.get_read_offset()) + pos - current_position; alire.unstack(alire_tmp); if(!alire.is_zero()) throw SRC_BUG; // we should be in the range of a U_I lus_data.front()->clear_data.rewind_read(alire_tmp); current_position = pos; found = true; } else { current_position += alire; tas->put(move(lus_data.front())); lus_data.pop_front(); lus_flags.pop_front(); if(current_position == pos && !lus_data.empty()) found = true; } } } return found; } void parallel_tronconneuse::run_threads() { if(t_status != thread_status::dead) throw SRC_BUG; if(!scatter) throw SRC_BUG; scatter->reset(); if(!gather) throw SRC_BUG; gather->reset(); tas->put(lus_data); lus_data.clear(); lus_flags.clear(); lus_eof = false; check_bytes_to_skip = true; deque >::iterator it = travailleur.begin(); while(it != travailleur.end()) { if((*it) != nullptr) (*it)->run(); else throw SRC_BUG; ++it; } switch(get_mode()) { case gf_read_only: if(!crypto_reader) throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); else crypto_reader->run(); break; case gf_write_only: if(!crypto_writer) throw Ememory("parallel_tronconneuse::parallel_tronconneuse"); else crypto_writer->run(); waiter->wait(); // release all threads // in write mode sub-threads // are not pending for an order // but for data to come and tread break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } // all subthreads are pending on waiter barrier t_status = thread_status::suspended; } void parallel_tronconneuse::stop_threads() { if(t_status == thread_status::dead) return; if(ignore_stop_acks > 0) { if(!purge_unack_stop_order()) throw SRC_BUG; // we just need to remove the stop pending order // to be sure the threads are stop and in condition // to receive the die order } if(get_mode() == gf_read_only) send_read_order(tronco_flags::die); else send_write_order(tronco_flags::die); } void parallel_tronconneuse::join_workers_only() { deque >::iterator it = travailleur.begin(); while(it != travailleur.end()) { if((*it) != nullptr) (*it)->join(); else throw SRC_BUG; ++it; } } void parallel_tronconneuse::join_threads() { try { if(get_mode() == gf_read_only) crypto_reader->join(); // may propagate exception thrown in child thread else crypto_writer->join(); // may propagate exception thrown in child thread } catch(...) { join_workers_only(); t_status = thread_status::dead; throw; } join_workers_only(); t_status = thread_status::dead; } U_I parallel_tronconneuse::get_heap_size(U_I num_workers) { U_I ratelier_size = get_ratelier_size(num_workers); U_I heap_size = ratelier_size * 2 + num_workers + 1 + ratelier_size + 2; // each ratelier can be full of crypto_segment and at the same // time, each worker could hold a crypto_segment, the below thread // as well and the main thread for parallel_tronconneuse could hold // a deque of the size of the ratelier plus 2 more crypto_segments return heap_size; } ///////////////////////////////////////////////////// // // read_blow class implementation // // void read_below::inherited_run() { try { if(!waiting) throw SRC_BUG; else waiting->wait(); // initial sync before starting reading data // initializing clear and encryted buf size // needed by get_ready_for_new_offset ptr = tas->get(); if(ptr->clear_data.get_max_size() < clear_buf_size) { tas->put(move(ptr)); throw SRC_BUG; } encrypted_buf_size = ptr->crypted_data.get_max_size(); tas->put(move(ptr)); index_num = get_ready_for_new_offset(); work(); } catch(...) { send_flag_to_workers(tronco_flags::exception_below); throw; // this will end the current thread with an exception } } void read_below::work() { bool end = false; do { switch(flag) { // flag can be modifed anytime by the main thread, we must not assume // its value in a give case to be equal to the what the switch directive // pointed to case tronco_flags::die: if(reof) // eof collided with a received order, continuing the eof process flag = tronco_flags::eof; else { send_flag_to_workers(tronco_flags::die); end = true; } break; case tronco_flags::eof: send_flag_to_workers(tronco_flags::eof); waiting->wait(); if(flag == tronco_flags::eof) throw SRC_BUG; // new order has not been set if(flag == tronco_flags::normal) index_num = get_ready_for_new_offset(); reof = false; // assuming condition has changed break; case tronco_flags::stop: if(reof) // eof collided with a received order, continuing the eof process flag = tronco_flags::eof; else { send_flag_to_workers(tronco_flags::stop); waiting->wait(); // We are suspended here until the caller has set new orders switch(flag) { case tronco_flags::die: break; case tronco_flags::normal: index_num = get_ready_for_new_offset(); break; case tronco_flags::stop: break; // possible if partial purging the lus_data by the main thread case tronco_flags::data_error: throw SRC_BUG; case tronco_flags::eof: throw SRC_BUG; default: throw SRC_BUG; } } break; case tronco_flags::normal: if(ptr) throw SRC_BUG; // show not have a block at this stage if(!reof) { ptr = tas->get(); // obtaining a new segment from the heap ptr->reset(); ptr->crypted_data.set_data_size(encrypted->read(ptr->crypted_data.get_addr(), ptr->crypted_data.get_max_size())); if( ! ptr->crypted_data.is_full()) // we have reached eof { if(trailing_clear_data != nullptr) // and we have a callback to remove clear data at eof { unique_ptr tmp = nullptr; remove_trailing_clear_data_from_encrypted_buf(crypt_offset, version, initial_shift, trailing_clear_data, ptr, tmp, reof); } reof = true; // whatever the callback modified, we could not read the whole block // so we are at end of file, reof should be set to true in any case } if(ptr->crypted_data.get_data_size() > 0) { ptr->block_index = index_num++; crypt_offset += ptr->crypted_data.get_data_size(); workers->scatter(ptr, static_cast(tronco_flags::normal)); } else tas->put(move(ptr)); } else flag = tronco_flags::eof; break; case tronco_flags::data_error: throw SRC_BUG; case tronco_flags::exception_below: throw SRC_BUG; case tronco_flags::exception_worker: throw SRC_BUG; default: throw SRC_BUG; } } while(!end); } infinint read_below::get_ready_for_new_offset() { infinint ret; position_clear2crypt(skip_to, crypt_offset, clear_flow_start, pos_in_flow, ret); if(!encrypted->skip(crypt_offset + initial_shift)) reof = true; else reof = false; return ret; } void read_below::send_flag_to_workers(tronco_flags theflag) { unique_ptr ptr; for(unsigned int i = 0; i < num_w; ++i) { ptr = tas->get(); ptr->reset(); workers->scatter(ptr, static_cast(theflag)); } } void read_below::position_clear2crypt(const infinint & pos, infinint & file_buf_start, infinint & clear_buf_start, infinint & pos_in_buf, infinint & block_num) { euclide(pos, clear_buf_size, block_num, pos_in_buf); file_buf_start = block_num * infinint(encrypted_buf_size); clear_buf_start = block_num * infinint(clear_buf_size); } ///////////////////////////////////////////////////// // // below_writer implementation // // void write_below::inherited_run() { error = false; error_block = 0; cur_num_w = num_w; try { if(!waiting || !workers) throw SRC_BUG; else waiting->wait(); // initial sync with other threads work(); } catch(...) { error = true; try { work(); } catch(...) { // ignore further exceptions } throw; } } void write_below::work() { bool end = false; do { if(ones.empty()) { if(!flags.empty()) throw SRC_BUG; workers->gather(ones, flags); } if(ones.empty() || flags.empty()) { if(!error) throw SRC_BUG; else end = true; // avoiding endless loop } switch(static_cast(flags.front())) { case tronco_flags::normal: if(!error) { error_block = ones.front()->block_index; encrypted->write(ones.front()->crypted_data.get_addr(), ones.front()->crypted_data.get_data_size()); } break; case tronco_flags::stop: if(!error) throw SRC_BUG; break; case tronco_flags::eof: if(!error) throw SRC_BUG; break; case tronco_flags::data_error: // error reported by a worker while encrypting // we ignore it and set the flag error = true; break; case tronco_flags::die: // read num dies and push them back to tas --cur_num_w; if(cur_num_w == 0) end = true; break; case tronco_flags::exception_below: if(!error) throw SRC_BUG; break; case tronco_flags::exception_worker: error = true; break; default: if(!error) throw SRC_BUG; } tas->put(move(ones.front())); ones.pop_front(); flags.pop_front(); } while(!end); } ///////////////////////////////////////////////////// // // crypto_worker implementation // // void crypto_worker::inherited_run() { try { waiting->wait(); // initial sync before starting working work(); } catch(...) { try { abort = status::inform; work(); } catch(...) { // we ignore exception here } throw; // propagating the original exception } } void crypto_worker::work() { bool end = false; signed int flag; do { ptr = reader->worker_get_one(slot, flag); switch(static_cast(flag)) { case tronco_flags::normal: switch(abort) { case status::fine: if(!ptr) throw SRC_BUG; try { if(do_encrypt) { ptr->crypted_data.set_data_size(crypto->encrypt_data(ptr->block_index, ptr->clear_data.get_addr(), ptr->clear_data.get_data_size(), ptr->clear_data.get_max_size(), ptr->crypted_data.get_addr(), ptr->crypted_data.get_max_size())); ptr->crypted_data.rewind_read(); } else { ptr->clear_data.set_data_size(crypto->decrypt_data(ptr->block_index, ptr->crypted_data.get_addr(), ptr->crypted_data.get_data_size(), ptr->clear_data.get_addr(), ptr->clear_data.get_max_size())); ptr->clear_data.rewind_read(); } } catch(Erange & e) { flag = static_cast(tronco_flags::data_error); // we will push the block with this flag data_error } break; case status::inform: flag = static_cast(tronco_flags::exception_worker); abort = status::sent; break; case status::sent: break; default: // we can't report this problem // because we can trigger an exception // only when abort equals status fine break; } writer->worker_push_one(slot, ptr, flag); break; case tronco_flags::stop: case tronco_flags::eof: writer->worker_push_one(slot, ptr, flag); waiting->wait(); break; case tronco_flags::die: end = true; writer->worker_push_one(slot, ptr, flag); break; case tronco_flags::data_error: if(abort == status::fine) throw SRC_BUG; // should not receive a block with that flag break; case tronco_flags::exception_below: end = true; writer->worker_push_one(slot, ptr, flag); break; case tronco_flags::exception_worker: if(abort == status::fine) throw SRC_BUG; break; default: throw SRC_BUG; } } while(!end); } ///////////////////////////////////////////////////// // // static routines implementation // // static void remove_trailing_clear_data_from_encrypted_buf(const infinint & read_offset, ///< offset of the first byte of the 'first' segment const archive_version & reading_ver, ///< read archive format version const infinint & initial_shift, ///< amount of free bytes before encrypted ones infinint (*callback)(generic_file & below, const archive_version& reading_version), unique_ptr & first, ///< pointer to a existing segment unique_ptr & opt_next, ///< in option pointer to the segment following the first in the crypted data bool & reof) { infinint clear_offset = 0; memory_file tmp; if(callback == nullptr) throw SRC_BUG; if(!first) throw SRC_BUG; tmp.write(first->crypted_data.get_addr(), first->crypted_data.get_data_size()); if(opt_next) tmp.write(opt_next->crypted_data.get_addr(), opt_next->crypted_data.get_data_size()); if(callback == nullptr) throw SRC_BUG; clear_offset = (*callback)(tmp, reading_ver); if(clear_offset >= initial_shift) clear_offset -= initial_shift; // now clear_offset can be compared to read_offset else return; if(read_offset >= clear_offset) // all data in encrypted_buf is clear data { first->reset(); if(opt_next) opt_next->reset(); // empty the second segment reof = true; } else { // finding where clear_data is located in the providing buffer(s) U_I nouv_buf_data = 0; clear_offset -= read_offset; clear_offset.unstack(nouv_buf_data); if(!clear_offset.is_zero()) throw SRC_BUG; // cannot handle that integer as U_32 while this number should be less than encrypted_buf_size which is a U_32 if(nouv_buf_data <= first->crypted_data.get_data_size()) { first->crypted_data.set_data_size(nouv_buf_data); first->crypted_data.rewind_read(); if(opt_next) opt_next->reset(); reof = true; } else throw SRC_BUG; // more encrypted data than could be read so far! } } } // end of namespace dar-2.7.15/src/libdar/real_infinint.hpp0000644000175000017500000002674514636066467014713 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file real_infinint.hpp /// \brief the original infinint class implementation /// \ingroup API /// /// the infinint class implementation defined in this module can /// handle arbitrary large positive integer numbers #ifndef REAL_INFININT_HPP #define REAL_INFININT_HPP #include "../my_config.h" extern "C" { #if HAVE_SYS_TYPES_H #include #endif } // end extern "C" #include #include "integers.hpp" #include "int_tools.hpp" #include "proto_generic_file.hpp" #include "storage.hpp" #define ZEROED_SIZE 50 namespace libdar { /// \addtogroup API /// @{ /// the arbitrary large positive integer class /// can only handle positive integer numbers /// \note if you just want to convert an infinint /// integer to its decimal representation see the /// class libdar::deci class infinint { public : #if SIZEOF_OFF_T > SIZEOF_TIME_T #if SIZEOF_OFF_T > SIZEOF_SIZE_T infinint(off_t a = 0) { infinint_from(a); }; #else infinint(size_t a = 0) { infinint_from(a); }; #endif #else #if SIZEOF_TIME_T > SIZEOF_SIZE_T infinint(time_t a = 0) { infinint_from(a); }; #else infinint(size_t a = 0) { infinint_from(a); }; #endif #endif /// read an infinint from a file infinint(proto_generic_file & x); infinint(const infinint & ref) { copy_from(ref); } infinint(infinint && ref) noexcept { field = nullptr; move_from(std::move(ref)); }; infinint & operator = (const infinint & ref) { detruit(); copy_from(ref); return *this; }; infinint & operator = (infinint && ref) noexcept { move_from(std::move(ref)); return *this; } ~infinint() { detruit(); }; void dump(proto_generic_file &x) const; // write byte sequence to file void read(proto_generic_file &f) { detruit(); build_from_file(f); }; infinint & operator += (const infinint & ref); infinint & operator -= (const infinint & ref); infinint & operator *= (unsigned char arg); infinint & operator *= (const infinint & ref); template infinint power(const T & exponent) const; inline infinint & operator /= (const infinint & ref); inline infinint & operator %= (const infinint & ref); infinint & operator &= (const infinint & ref); infinint & operator |= (const infinint & ref); infinint & operator ^= (const infinint & ref); infinint & operator >>= (U_32 bit); infinint & operator >>= (infinint bit); infinint & operator <<= (U_32 bit); infinint & operator <<= (infinint bit); infinint operator ++(int a) { infinint ret = *this; ++(*this); return ret; }; infinint operator --(int a) { infinint ret = *this; --(*this); return ret; }; infinint & operator ++() { return *this += 1; }; infinint & operator --() { return *this -= 1; }; U_32 operator % (U_32 arg) const { return modulo(arg); }; /// convert infinint to standard interger types /// \note increment the argument up to a legal value for /// its storage type and decrement the object in consequence /// note that the initial value of the argument is not ignored! /// when the object is null the value of the argument is unchanged template void unstack(T &v) { infinint_unstack_to(v); } /// it returns number of byte of information necessary to store the integer infinint get_storage_size() const noexcept { return field->size(); }; /// return in little endian order the information byte storing the integer unsigned char operator [] (const infinint & position) const; /// \return true when the object is zero (more efficient than integer comparison) bool is_zero() const; friend bool operator < (const infinint &, const infinint &); friend bool operator == (const infinint &, const infinint &); friend bool operator > (const infinint &, const infinint &); friend bool operator <= (const infinint &, const infinint &); friend bool operator != (const infinint &, const infinint &); friend bool operator >= (const infinint &, const infinint &); friend void euclide(infinint a, const infinint &b, infinint &q, infinint &r); static bool is_system_big_endian(); private : static constexpr int TG = 4; enum endian { big_endian, little_endian, not_initialized }; using group = unsigned char[TG]; storage *field; bool is_valid() const noexcept; void build_from_file(proto_generic_file & x); void reduce(); // put the object in canonical form : no leading byte equal to zero void copy_from(const infinint & ref); void move_from(infinint && ref) noexcept { std::swap(field, ref.field); }; void detruit(); void make_at_least_as_wider_as(const infinint & ref); template void infinint_from(T a); template T max_val_of(T x); template void infinint_unstack_to(T &a); template T modulo(T arg) const; signed int difference(const infinint & b) const; // gives the sign of (*this - arg) but only the sign ! ///////////////////////// // static statments // static endian used_endian; static U_8 zeroed_field[ZEROED_SIZE]; static void setup_endian(); }; #define OPERATOR(OP) inline bool operator OP (const infinint &a, const infinint &b) \ { \ return a.difference(b) OP 0; \ } OPERATOR(<) OPERATOR(>) OPERATOR(<=) OPERATOR(>=) OPERATOR(==) OPERATOR(!=) infinint operator + (const infinint &, const infinint &); infinint operator - (const infinint &, const infinint &); infinint operator * (const infinint &, const infinint &); infinint operator * (const infinint &, const unsigned char); infinint operator * (const unsigned char, const infinint &); infinint operator / (const infinint &, const infinint &); infinint operator % (const infinint &, const infinint &); infinint operator & (const infinint & a, const infinint & bit); infinint operator | (const infinint & a, const infinint & bit); infinint operator ^ (const infinint & a, const infinint & bit); infinint operator >> (const infinint & a, U_32 bit); infinint operator >> (const infinint & a, const infinint & bit); infinint operator << (const infinint & a, U_32 bit); infinint operator << (const infinint & a, const infinint & bit); void euclide(infinint a, const infinint &b, infinint &q, infinint &r); template inline void euclide(T a, T b, T & q, T &r) { q = a/b; r = a%b; } inline infinint & infinint::operator /= (const infinint & ref) { *this = *this / ref; return *this; } inline infinint & infinint::operator %= (const infinint & ref) { *this = *this % ref; return *this; } ///////////////////////////////////////////////////// ///////////////// TEMPLATE BODIES /////////////////// ///////////////////////////////////////////////////// template infinint infinint::power(const T & exponent) const { infinint ret = 1; for(T count = 0; count < exponent; ++count) ret *= *this; return ret; } template T infinint::modulo(T arg) const { infinint tmp = *this % infinint(arg); T ret = 0; unsigned char *debut = (unsigned char *)(&ret); unsigned char *ptr = debut + sizeof(T) - 1; storage::iterator it = tmp.field->rbegin(); while(it != tmp.field->rend() && ptr >= debut) { *ptr = *it; --ptr; --it; } // checking for overflow (should never occur, but for sanity, we check it anyway) while(it != tmp.field->rend()) // field may not be reduced (some zeros are leading) { if(*it != 0) throw SRC_BUG; // could not put all the data in the returned value ! --it; } if(used_endian == little_endian) int_tools_swap_bytes(debut, sizeof(T)); return ret; } template void infinint::infinint_from(T a) { U_I size = sizeof(a); S_I direction = +1; unsigned char *ptr, *fin; if(used_endian == not_initialized) setup_endian(); if(used_endian == little_endian) { direction = -1; ptr = (unsigned char *)(&a) + (size - 1); fin = (unsigned char *)(&a) - 1; } else { direction = +1; ptr = (unsigned char *)(&a); fin = (unsigned char *)(&a) + size; } while(ptr != fin && *ptr == 0) { ptr += direction; --size; } if(size == 0) { size = 1; ptr -= direction; } field = new (std::nothrow) storage(size); if(field != nullptr) { storage::iterator it = field->begin(); while(ptr != fin) { *it = *ptr; ++it; ptr += direction; } if(it != field->end()) throw SRC_BUG; // size mismatch in this algorithm } else throw Ememory("template infinint::infinint_from"); } template T infinint::max_val_of(T x) { x = 0; x = ~x; if(x <= 0) // T is a signed integer type. Note that it should be "x < 0" but to avoid compiler warning when T is unsigned it does not hurt having "x <= 0" here { x = 1; x = int_tools_rotate_right_one_bit(x); x = ~x; } return x; } template void infinint::infinint_unstack_to(T & a) { // T is supposed to be an unsigned "integer" // (ie.: sizeof() returns the width of the storage bit field and no sign bit is present) // Note : static here avoids the recalculation of max_T at each call static const T max_T = max_val_of(a); infinint step = max_T - a; if(*this < step) { T transfert = 0; unsigned char *debut = (unsigned char *)&transfert; unsigned char *ptr = debut + sizeof(transfert) - 1; storage::iterator it = field->rbegin(); while(ptr >= debut && it != field->rend()) { *ptr = *it; --ptr; --it; } if(used_endian == little_endian) int_tools_swap_bytes(debut, sizeof(transfert)); a += transfert; *this -= *this; } else { *this -= step; a = max_T; } } } // end of namespace #endif dar-2.7.15/src/libdar/tuyau_global.hpp0000644000175000017500000001150014636066467014540 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file tuyau_global.hpp /// \brief seekable pipe on top of another fichier_global /// /// objects of this class provide the mean to give position and seek forward /// features on a underlying system object that only support sequential reads /// like pipes and some special devices. The class tuyau does the same but /// is a mix of many features and very bundled to the system calls. It would /// worth in the future revising class tuyau to remove from it the features now /// carried by tuyau_global (to be considered as class zapette also need these /// skip()/get_position() emulated features, while it is completely independent /// from entrepot class hierarchy). /// \ingroup Private #ifndef TUYAU_GLOBAL_HPP #define TUYAU_GLOBAL_HPP #include "../my_config.h" #include "tuyau.hpp" #include "fichier_global.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the tuyau_global provides skip()/get_position() features on top of pipe-like object class tuyau_global: public fichier_global { public: /// constructor /// \param[in] dialog for user interaction requested by fichier_global /// \param[in] x_ptr the hidden/underlying generic_file to provide seekable feature for /// \note the object pointed to by x_ptr passes under the responsibility of the tuyau_global object, /// it will be automatically deleted when no more needed by the tuyau_global object. tuyau_global(const std::shared_ptr & dialog, fichier_global *x_ptr); /// copy constructor tuyau_global(tuyau_global & ref) = delete; /// move constructor tuyau_global(tuyau_global && ref) = delete; /// assignment operator tuyau_global & operator = (const tuyau_global & ref) = delete; /// move assignment operator tuyau_global & operator = (tuyau_global && ref) = delete; /// destructor ~tuyau_global() { detruit(); }; // inherited from fichier_global virtual void change_ownership(const std::string & user, const std::string & group) override { ptr->change_ownership(user, group); }; virtual void change_permission(U_I perm) override { ptr->change_permission(perm); }; virtual infinint get_size() const override { return ptr->get_size(); }; virtual void fadvise(advise adv) const override { ptr->fadvise(adv); }; // inherited from generic_file grand-parent class virtual bool skippable(skippability direction, const infinint & amount) override { return ptr->skippable(direction, amount); }; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return ptr->truncatable(pos); }; virtual infinint get_position() const override { return current_pos; }; protected: // inherited from fichier_global virtual U_I fichier_global_inherited_write(const char *a, U_I size) override; virtual bool fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) override; // inherted from generic_file virtual void inherited_read_ahead(const infinint & amount) override { ptr->read_ahead(amount); }; virtual void inherited_truncate(const infinint & pos) override { ptr->truncate(pos); }; virtual void inherited_sync_write() override { ptr->sync_write(); }; virtual void inherited_flush_read() override { ptr->flush_read(); }; virtual void inherited_terminate() override { ptr->terminate(); }; private: static const U_I buffer_size = 102400; fichier_global *ptr; ///< points to the underlying object infinint current_pos; ///< record the current offset char buffer[buffer_size]; ///< to skip emulation done by reading data void detruit(); U_I read_and_drop(U_I bytes); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/smart_pointer.hpp0000644000175000017500000001315514636066467014747 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file smart_pointer.hpp /// \brief template class implementing memory efficient smart pointer /// \ingroup Private /// \note Why not using std::shared_ptr? because I needed to have a warranty on the /// scalability in term of max number of smart_pointers that can be bound to an object #ifndef SMART_POINTER_HPP #define SMART_POINTER_HPP #include "../my_config.h" #include "infinint.hpp" #include "erreurs.hpp" namespace libdar { /// \addtogroup Private /// @{ /// class which holds the address of the allocated memory for many smart_pointers /// \note it should not be used directly, rather see below the smart_pointer class template template class smart_node { public: /// \note the given pointed to object passes under the responsibility of the smart_node smart_node(T *arg): ptr(arg), count_ref(0) { if(arg == nullptr) throw SRC_BUG; }; smart_node(const smart_node & ref) = delete; smart_node(smart_node && ref) noexcept = delete; smart_node & operator = (const smart_node & ref) = delete; smart_node & operator = (smart_node && ref) = delete; ~smart_node() noexcept(false) { if(ptr != nullptr) delete ptr; if(!count_ref.is_zero()) throw SRC_BUG; }; void add_ref() { ++count_ref; }; void del_ref() { if(count_ref.is_zero()) throw SRC_BUG; --count_ref; if(count_ref.is_zero()) delete this; }; T & get_val() { return *ptr; }; private: T *ptr; infinint count_ref; }; /// smart pointer class to be used to automagically manage multiple time pointed to address /// this class tend to mimic normal pointer with the additional feature of automatically releasing /// the pointed to object when no more smart_pointer point to it. In consequence: /// - it must not be used to point to non dynamically allocated memory using the "new" operator, /// - pointed to memory must never be deleted manually, the last smart_pointer will do it at its /// destruction time /// \note IMPORTANT: smart_pointer cannot be shared between different threads, for efficiency, /// the smart_pointer nor the pointed to object is protected against concurrent access of several /// threads template class smart_pointer { public: /// creates a smart_pointer equivalent to a pointer to NULL smart_pointer() { ptr = nullptr; }; /// creates a smart_pointer pointing to an allocated memory /// \param[in] arg is the address of the allocated memory the smart_pointer must manage, /// nullptr is allowed and lead to the same behavior as the constructor without argument /// \note the given pointed to object, passes under the responsibility of the smart_pointer /// and must not be deleted any further smart_pointer(T *arg) { if(arg != nullptr) { ptr = new (std::nothrow) smart_node(arg); if(ptr == nullptr) throw Ememory("smart_pointer::smart_pointer"); ptr->add_ref(); } else ptr = nullptr; }; /// copy constructor smart_pointer(const smart_pointer & ref) { ptr = ref.ptr; if(ptr != nullptr) ptr->add_ref(); }; /// move constructor smart_pointer(smart_pointer && ref) noexcept { ptr = ref.ptr; ref.ptr = nullptr; }; /// destructor ~smart_pointer() { if(ptr != nullptr) ptr->del_ref(); }; /// assignment operator smart_pointer & operator = (const smart_pointer & ref) { if(ref.ptr != ptr) { if(ref.ptr != nullptr) { if(ptr != nullptr) ptr->del_ref(); ptr = ref.ptr; ptr->add_ref(); } else { ptr->del_ref(); // ptr is no nullptr because ref.ptr != ptr ptr = nullptr; } } return *this; }; /// move assignment operator smart_pointer & operator = (smart_pointer && ref) { if(ptr != ref.ptr) { if(ptr != nullptr) ptr->del_ref(); ptr = ref.ptr; ref.ptr = nullptr; } return *this; }; /// assignment operator from a base type pointer (not from a smart_pointer) /// \note choice has been not to overload/use operator= to avoid risk of error /// that would lead to create independent smart_pointer sets accidentally const smart_pointer & assign(T *arg) { smart_pointer tmp(arg); *this = tmp; return *this; } /// content-of operator T & operator *() const { if(ptr == nullptr) throw SRC_BUG; return ptr->get_val(); }; /// content-of field operator (when the pointed to object is a struct or class T* operator ->() const { if(ptr == nullptr) throw SRC_BUG; return &(ptr->get_val()); }; /// return whether the smart_pointer is pointing to nullptr bool is_null() const { return ptr == nullptr; }; private: smart_node *ptr; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/filesystem_restore.hpp0000644000175000017500000001541614636066467016012 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filesystem_restore.hpp /// \brief class filesystem_restores create inodes from a flow of libdar objects /// \ingroup Private #ifndef FILESYSTEM_RESTORE_HPP #define FILESYSTEM_RESTORE_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_STAT_H #include #endif } // end extern "C" #include #include "crit_action.hpp" #include "fsa_family.hpp" #include "cat_all_entrees.hpp" #include "filesystem_hard_link_read.hpp" #include "filesystem_hard_link_write.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// receive the flow of inode from the restoration filtering routing and promotes these to real filesystem objects class filesystem_restore : public filesystem_hard_link_write, public filesystem_hard_link_read { public: /// constructor filesystem_restore(const std::shared_ptr & dialog, const path & root, bool x_warn_overwrite, bool x_info_details, const mask & x_ea_mask, comparison_fields what_to_check, bool x_warn_remove_no_match, bool empty, const crit_action *x_overwrite, bool x_only_overwrite, const fsa_scope & scope); /// copy constructor is forbidden filesystem_restore(const filesystem_restore & ref) = delete; /// move constructor is forbidden filesystem_restore(filesystem_restore && ref) = delete; /// assignment operator is forbidden filesystem_restore & operator = (const filesystem_restore & ref) = delete; /// move operator is forbidden filesystem_restore & operator = (filesystem_restore && ref) = delete; /// destructor ~filesystem_restore() { restore_stack_dir_ownership(); detruire(); }; /// reset the writing process for the current object void reset_write(); using action_done_for_data = enum { done_data_restored, //< data has been restored to filesystem done_no_change_no_data, //< no change in filesystem because no data present in archive done_no_change_policy, //< no change in filesystem because of overwiting policy decision done_data_removed //< data (= whole inode) removed from filesystem }; /// restore a libdar object to a filesystem entry both data and EA /// \param[in] x is the libdar object to restore /// \param[out] data_restored true if data has been restored (inode or hard link created), false if either there is no data to restore or if this action is forbidden by the overwriting policy /// \param[out] ea_restored true if EA has been restored, false if either no EA to restore or if forbidden by overwriting policy /// \param[out] data_created true if data has been restored leading to file creation, false in any other case /// \param[out] hard_link true when data_restored is true and only a hard link to an already existing inode has been created /// \param[out] fsa_restored true if FSA has been restored, false if either no FSA to restore or if forbidden by overwriting policy /// \note any failure to restore data or EA that is not due to its absence in "x" nor to an interdiction from the overwriting policy is signaled /// through an exception. void write(const cat_entree *x, action_done_for_data & data_restored, bool & ea_restored, bool & data_created, bool & hard_link, bool & fsa_restored); /// ask for no warning or user interaction for the next write operation /// \note this is used when a file has been saved several times due to its changes at the time of the backup /// and is restored in sequential read. Restoring each failed backup would lead to ask each time the /// actions to take about overwriting... anoying for the user void ignore_overwrite_restrictions_for_next_write() { ignore_over_restricts = true; }; private: class stack_dir_t : public cat_directory { public: stack_dir_t(const cat_directory & ref, bool restore) : cat_directory(ref) { restore_date = restore; }; bool get_restore_date() const { return restore_date; }; void set_restore_date(bool val) { restore_date = val; }; private: bool restore_date; }; path *fs_root; bool info_details; mask *ea_mask; bool warn_overwrite; comparison_fields what_to_check; bool warn_remove_no_match; std::deque stack_dir; path *current_dir; bool empty; bool ignore_over_restricts; const crit_action *overwrite; bool only_overwrite; void detruire(); void restore_stack_dir_ownership(); user_interaction & get_ui() const { return filesystem_hard_link_read::get_ui(); }; std::shared_ptr get_pointer() const { return filesystem_hard_link_read::get_pointer(); }; // subroutines of write() /// perform action due to the overwriting policy when the "to be added" entry is a detruit object void action_over_remove(const cat_inode *in_place, const cat_detruit *to_be_added, const std::string & spot, over_action_data action); /// perform action for data due to the overwriting policy when the "to be added" entry is not a cat_detruit void action_over_data(const cat_inode *in_place, const cat_nomme *to_be_added, const std::string & spot, over_action_data action, action_done_for_data & data_done); /// perform action for EA due to overwriting policy bool action_over_ea(const cat_inode *in_place, const cat_nomme *to_be_added, const std::string & spot, over_action_ea action); /// perform action for FSA due to overwriting policy bool action_over_fsa(const cat_inode *in_place, const cat_nomme *to_be_added, const std::string & spot, over_action_ea action); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/hash_fichier.cpp0000644000175000017500000001204114636067146014454 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "hash_fichier.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "path.hpp" using namespace std; namespace libdar { hash_fichier::hash_fichier(const shared_ptr & dialog, fichier_global *under, const std::string & under_filename, fichier_global *hash_file, hash_algo algo) : fichier_global(dialog, under->get_mode()) { if(under == nullptr) throw SRC_BUG; if(hash_file == nullptr) throw SRC_BUG; if(under->get_mode() == gf_read_write) throw SRC_BUG; if(hash_file->get_mode() != gf_write_only) throw SRC_BUG; only_hash = false; ref = under; hash_ref = hash_file; path tmp = under_filename; ref_filename = tmp.basename(); eof = false; hash_dumped = false; #if CRYPTO_AVAILABLE gcry_error_t err; hash_gcrypt = hash_algo_to_gcrypt_hash(algo); err = gcry_md_test_algo(hash_gcrypt); if(err != GPG_ERR_NO_ERROR) throw Erange("hash_fichier::hash_fichier",tools_printf(gettext("Error while initializing hash: Hash algorithm not available in libgcrypt: %s/%s"), gcry_strsource(err),gcry_strerror(err))); err = gcry_md_open(&hash_handle, hash_gcrypt, 0); // no need of secure memory here if(err != GPG_ERR_NO_ERROR) throw Erange("hash_fichier::hash_fichier",tools_printf(gettext("Error while creating hash handle: %s/%s"), gcry_strsource(err),gcry_strerror(err))); #else throw Ecompilation(gettext("Missing hashing algorithms support (which is part of strong encryption support, using libgcrypt)")); #endif } hash_fichier::~hash_fichier() { try { terminate(); } catch(...) { // ignore all errors } if(ref != nullptr) { delete ref; ref = nullptr; } if(hash_ref != nullptr) { delete hash_ref; hash_ref = nullptr; } } U_I hash_fichier::fichier_global_inherited_write(const char *a, U_I size) { #if CRYPTO_AVAILABLE if(eof) throw SRC_BUG; gcry_md_write(hash_handle, (const void *)a, size); if(!only_hash) ref->write(a, size); return size; #else throw Ecompilation(gettext("Missing hashing algorithms support (which is part of strong encryption support, using libgcrypt)")); #endif } bool hash_fichier::fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) { #if CRYPTO_AVAILABLE if(eof) throw SRC_BUG; read = ref->read(a, size); message = "BUG! This should never show!"; if(read > 0) gcry_md_write(hash_handle, (const void *)a, read); return true; #else throw Ecompilation(gettext("Missing hashing algorithms support (which is part of strong encryption support, using libgcrypt)")); #endif } void hash_fichier::inherited_terminate() { ref->terminate(); if(!hash_dumped) { // avoids subsequent writings (yeld a bug report if that occurs) eof = true; // avoid a second run of dump_hash() hash_dumped = true; try { #if CRYPTO_AVAILABLE // first we obtain the hash result; const unsigned char *digest = gcry_md_read(hash_handle, hash_gcrypt); const U_I digest_size = gcry_md_get_algo_dlen(hash_gcrypt); try { string hexa = tools_string_to_hexa(string((char *)digest, digest_size)); if(hash_ref == nullptr) throw SRC_BUG; hash_ref->write((const char *)hexa.c_str(), hexa.size()); hash_ref->write(" ", 2); // two spaces sperator used by md5sum and sha1sum hash_ref->write(ref_filename.c_str(), ref_filename.size()); hash_ref->write("\n", 1); // we finish by a new-line character hash_ref->terminate(); } catch(Egeneric & e) { throw Erange("hash_fichier::dump_hash", gettext("Failed writing down the hash: ") + e.get_message()); } #endif // no #else clause (routine used from constructor, if binary lack support // for strong encryption this has already been returned to the user // and we must not trouble the destructor for that } catch(...) { #if CRYPTO_AVAILABLE gcry_md_close(hash_handle); #endif throw; } #if CRYPTO_AVAILABLE gcry_md_close(hash_handle); #endif } } } // end of namespace dar-2.7.15/src/libdar/proto_generic_file.hpp0000644000175000017500000000566114636066467015722 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file proto_generic_file.hpp /// \brief precursor class of generic_file used to avoid cyclic dependencies with storage and infinint /// \ingroup Private /// \note API included module due to dependencies #ifndef PROTO_GENERIC_FILE_HPP #define PROTO_GENERIC_FILE_HPP #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif } // end extern "C" #include "integers.hpp" #include "erreurs.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// ancestor class of generic_file /// this class exist to avoid cyclic dependency between generic_file and infinint class proto_generic_file { public : proto_generic_file() {}; /// copy constructor proto_generic_file(const proto_generic_file &ref) = default; /// move constructor proto_generic_file(proto_generic_file && ref) noexcept = default; /// assignment operator proto_generic_file & operator = (const proto_generic_file & ref) = default; /// move operator proto_generic_file & operator = (proto_generic_file && ref) noexcept = default; /// virtual destructor /// \note this let inherited destructor to be called even from a proto_generic_file pointer to an inherited class virtual ~proto_generic_file() noexcept(false) {}; /// read data from the proto_generic_file /// \param[in, out] a is where to put the data to read /// \param[in] size is how much data to read /// \return the exact number of byte read. /// \note read as much as requested data, unless EOF is met (only EOF can lead to reading less than requested data) /// \note EOF is met if read() returns less than size virtual U_I read(char *a, U_I size) = 0; /// write data to the proto_generic_file /// \note throws a exception if not all data could be written as expected virtual void write(const char *a, U_I size) = 0; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/generic_to_global_file.hpp0000644000175000017500000001145214636066467016514 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file generic_to_global_file.hpp /// \brief this class provides an fichier_global interface for any type of generic_file object /// /// the main use is to be able to have hash_fichier working on other type of objects /// than fichier_global. The drawback is that all file specific operation is ignored /// (file ownership, permission...). The other point is the get_size() method which returns /// the current position of the read or write cursor, not the total available amount of data /// \ingroup Private #ifndef GENERIC_TO_GLOBAL_FILE_HPP #define GENERIC_TO_GLOBAL_FILE_HPP #include "fichier_global.hpp" namespace libdar { /// \addtogroup Private /// @{ /// provides a fichier_global interface for any type of generic_file class generic_to_global_file : public fichier_global { public: /// Constructors & Destructor /// \param[in] dialog for user interaction /// \param[in] d pointer to the generic_file object to provide a fichier_global interface to. /// \param[in] mode read/write mode to use /// \note the pointed to generic_file object must survive the whole live of the generic_to_gloabl_file. This generic_file is not owned nor deleted by the generic_to_global_file object that points to it. generic_to_global_file(const std::shared_ptr & dialog, generic_file *d, gf_mode mode): fichier_global(dialog, mode) { if(d == nullptr) throw SRC_BUG; if(d->get_mode() != gf_read_write && d->get_mode() != mode) throw SRC_BUG; data = d; }; generic_to_global_file(const generic_to_global_file & ref) = default; generic_to_global_file(generic_to_global_file && ref) noexcept = default; generic_to_global_file & operator = (const generic_to_global_file & ref) = default; generic_to_global_file & operator = (generic_to_global_file && ref) noexcept = default; ~generic_to_global_file() = default; // virtual method inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override { return data->skippable(direction, amount); } virtual bool skip(const infinint & pos) override { return data->skip(pos); }; virtual bool skip_to_eof() override { return data->skip_to_eof(); }; virtual bool skip_relative(S_I x) override { return data->skip_relative(x); }; virtual bool truncatable(const infinint & pos) const override { return data->truncatable(pos); }; virtual infinint get_position() const override { return data->get_position(); }; // virtual method inherited from fichier_global virtual void change_ownership(const std::string & user, const std::string & group) override {}; virtual void change_permission(U_I perm) override {}; virtual infinint get_size() const override { return data->get_position(); }; //< yes, this is the drawback of this template class convertion, get_size() does not return the real size of the object virtual void fadvise(advise adv) const override {}; protected: // virtual method inherited from generic_file virtual void inherited_truncate(const infinint & pos) override { data->truncate(pos); }; virtual void inherited_read_ahead(const infinint & amount) override {}; // no optimization can be done here, we rely on the OS here virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override {}; // inherited from fichier_global virtual U_I fichier_global_inherited_write(const char *a, U_I size) override { data->write(a, size); return size; }; virtual bool fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) override { read = data->read(a, size); message = "THIS IS A BUG IN GENERIC_TO_GLOBAL_FILE, PLEASE REPORT TO THE MAINTAINER!"; return true; }; private: generic_file *data; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/semaphore.hpp0000644000175000017500000001324314636066467014042 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file semaphore.hpp /// \brief definition of class semaphore, used to manage invocation of backup hook for files /// \ingroup Private #ifndef SEMAPHORE_HPP #define SEMAPHORE_HPP #include "../my_config.h" #include "mem_ui.hpp" #include "mask.hpp" namespace libdar { /// \addtogroup Private /// @{ /// class semaphore /// Its action is to invoke the execute hook for each file that match the given mask /// Each file to backup has to be "raised()", which, if it matches the mask, leads /// to the execution of the execute hook with the proper substitution for that file /// in the "start" context. /// Then the backup can take place. /// When the backup is finished, the lower() method has to be called to trigger the /// execution of the hook with the proper substitution but in the "end" context. /// but, things are a bit complicated due to the handle of directories: /// If a directory is "raised()" and matches the mask, next calls to raise() do not trigger any /// hook execution even if the file match the mask, while saving into the directory /// that matched first. Instead, each new call to raise() increments an internal counter when /// a new directory is met, which is decremented when an "eod" is given to raised(). /// So it is important to feed raise() with any entry may it has to be saved or not. /// while lower() has only to be called when a file has been saved. /// This is only when this internal counters reaches zero, that the lower() call will /// trigger the execution for this first matched directory, of the hook in the "end" context. /// /// So the expected use is to give each file to be saved (including eod) to the raise() /// method, before eventually saving the file, and call the lower() method only for files that had to /// be saved once the backup is completed, may it be normally or due to an exception being thrown. class semaphore : public mem_ui { public: /// constructor /// \param[in] dialog for user interaction /// \param[in] backup_hook_file_execute is the string to execute, it can contains macros /// to be substitued, %f by filename, %p by path, %u by uid, %g by gid, and %c by the context, /// which is either "start" or "end". /// \param[in] backup_hook_file_mask defines the path+filename of entry that need to have /// the hook executed before and after their backup semaphore(const std::shared_ptr & dialog, const std::string & backup_hook_file_execute, const mask & backup_hook_file_mask); /// copy constructor semaphore(const semaphore & ref) : mem_ui(ref) { copy_from(ref); }; /// move constructor semaphore(semaphore && ref) noexcept: mem_ui(std::move(ref)) { nullifyptr(); move_from(std::move(ref)); }; /// assignment operator semaphore & operator = (const semaphore & ref) { detruit(); copy_from(ref); return *this; }; /// move operator semaphore & operator = (semaphore && ref) noexcept { move_from(std::move(ref)); return *this; }; /// destructor ~semaphore() { detruit(); }; /// to prepare a file for backup /// all file has to be given to this call, even the eod objects /// \param[in] path is the full path to the object /// \param[in] object is the object about to be saved /// \param[in] data_to_save tells whether this entry will have to /// be saved or just recursed into (directory for example) /// \note, if data_to_save is true, the lower() method is expected to be used /// before a next call to raise. For a directory this is only the call to lower() /// of the matching EOD that will trigger the hook execution in the "end" context. /// If instead data_to_save if false, no lower() call has to be done. void raise(const std::string & path, const cat_entree *object, bool data_to_save); /// to tell that the backup is completed for the last "raised" entry. void lower(); private: infinint count; ///< is the number of subdirectories currently saved in the last directory that matched the mask std::string chem; ///< path of the file that has to be call in the "end" context when count will drop to zero std::string filename; ///< filename of that same file infinint uid; ///< UID of that same file infinint gid; ///< GID of that same file unsigned char sig; ///< object type std::string execute; ///< command to execute const mask *match; ///< for which file to run the execute command void nullifyptr() noexcept { match = nullptr; }; std::string build_string(const std::string & context); void copy_from(const semaphore & ref); void move_from(semaphore && ref) noexcept; void detruit(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/range.cpp0000644000175000017500000000563214636066467013151 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "range.hpp" #include "deci.hpp" using namespace std; namespace libdar { void range::operator += (const range & ref) { list::const_iterator ref_it = ref.parts.begin(); while(ref_it != ref.parts.end()) { list::iterator it = parts.begin(); while(it != parts.end() && *it < *ref_it) ++it; if(it == parts.end()) parts.push_back(*ref_it); else if(*ref_it < *it) parts.insert(it, *ref_it); else { if(!it->overlaps_with(*ref_it)) throw SRC_BUG; it->merge_with(*ref_it); // we also have to test whether the next segment cannot be merged too list::iterator next = it; ++next; if(next != parts.end()) { if(it->overlaps_with(*next)) { it->merge_with(*next); parts.erase(next); } } } ++ref_it; } } string range::display() const { string ret = ""; list::const_iterator it = parts.begin(); while(it != parts.end()) { ret += it->display(); ++it; if(it != parts.end()) ret += ","; } if(ret.size() == 0) ret = ""; return ret; } bool range::read_next_segment(infinint & low, infinint & high) const { if(read_cursor != parts.end()) { low = read_cursor->get_low(); high = read_cursor->get_high(); ++read_cursor; return true; } else return false; } void range::segment::merge_with(const segment & ref) { if(*this <= ref) low = ref.low; else if(*this >= ref) high = ref.high; else if(contains(ref)) return; // nothing to do else if(ref.contains(*this)) *this = ref; else throw SRC_BUG; } string range::segment::display() const { string ret = ""; deci dl = low; if(low == high) ret = dl.human(); else { deci dh = high; ret = dl.human() + "-" + dh.human(); } return ret; } } // end of namespace dar-2.7.15/src/libdar/compressor_zstd.hpp0000644000175000017500000001134214636066467015315 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file compressor_zstd.hpp /// \brief streaming compression implementation for zstd algorithm /// \ingroup Private #ifndef COMPRESSOR_ZSTD_HPP #define COMPRESSOR_ZSTD_HPP #include "../my_config.h" extern "C" { #if HAVE_ZSTD_H #include #endif } #include "proto_compressor.hpp" namespace libdar { /// \addtogroup Private /// @{ /// compression class for zstd algorithms class compressor_zstd : public proto_compressor { public : compressor_zstd(generic_file & compressed_side, U_I compression_level = 9); // compressed_side is not owned by the object and will remains // after the objet destruction compressor_zstd(const compressor_zstd & ref) = delete; compressor_zstd(compressor_zstd && ref) noexcept = delete; compressor_zstd & operator = (const compressor_zstd & ref) = delete; compressor_zstd & operator = (compressor_zstd && ref) noexcept = delete; ~compressor_zstd(); virtual compression get_algo() const override; virtual void suspend_compression() override; virtual void resume_compression() override; virtual bool is_compression_suspended() const override { return suspended; }; // inherited from generic file virtual bool skippable(skippability direction, const infinint & amount) override { return compressed->skippable(direction, amount); }; virtual bool skip(const infinint & pos) override { compr_flush_write(); compr_flush_read(); clean_read(); return compressed->skip(pos); }; virtual bool skip_to_eof() override { compr_flush_write(); compr_flush_read(); clean_read(); return compressed->skip_to_eof(); }; virtual bool skip_relative(S_I x) override { compr_flush_write(); compr_flush_read(); clean_read(); return compressed->skip_relative(x); }; virtual bool truncatable(const infinint & pos) const override { return compressed->truncatable(pos); }; virtual infinint get_position() const override { return compressed->get_position(); }; protected : virtual void inherited_read_ahead(const infinint & amount) override { compressed->read_ahead(amount); }; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override { compr_flush_write(); }; virtual void inherited_flush_read() override { compr_flush_read(); clean_read(); }; virtual void inherited_terminate() override; private : generic_file *compressed; bool suspended; #if LIBZSTD_AVAILABLE ZSTD_CStream *comp; ZSTD_DStream *decomp; ZSTD_inBuffer inbuf; ZSTD_outBuffer outbuf; char *below_tampon; ///< used to hold in-transit data U_I below_tampon_size; ///< allocated size of tampon U_I above_tampon_size; ///< max size of input data bool flueof; ///< is EOF in read mode and flushed in write mode bool no_comp_data; ///< EOF in underlying layer in read mode #endif void reset_compr_engine(); ///< reset the compression engine ready for use void compr_flush_write(); // flush all data to compressed_side, and reset the compressor_zstd // for that additional write can be uncompresssed starting at this point. void compr_flush_read(); // reset decompression engine to be able to read the next block of compressed data // if not called, furthur read return EOF void clean_read(); // discard any byte buffered and not yet returned by read() void clean_write(); // discard any byte buffered and not yet wrote to compressed_side; /// from zstd void clear_inbuf(); void clear_outbuf(); void release_mem(); void setup_context(U_I compression_level); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/generic_rsync.hpp0000644000175000017500000001447614636066467014722 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file generic_rsync.hpp /// \brief class generic_rsync provides a generic_file interface to librsync /// \ingroup Private #ifndef GENERIC_RSYNC_HPP #define GENERIC_RSYNC_HPP #include "../my_config.h" extern "C" { #if HAVE_LIBRSYNC_H #include #endif } #include "generic_file.hpp" #include "erreurs.hpp" namespace libdar { /// \addtogroup Private /// @{ /// generic_file interface to librsync class generic_rsync : public generic_file { public: /// constructor for "signature" operation /// in this mode the generic_rsync object is read only, all data /// read from it is fetched unchanged from "below" while the signature is /// computed. The file signature is output to signature_storage /// \param[in] signature_storage is write only mode generic_file /// \param[in] signature_block_size the block len to use to build the signature /// \param[in] below is read only to fetch data from generic_rsync(generic_file *signature_storage, U_I signature_block_size, generic_file *below); /// constructor for "delta" operation /// in this mode the generic_rsync object is also read only, all data /// read from it is the resulting delta of the data read from "below" based /// the given base_signature. /// \param[in] base_signature is read only /// \param[in] below is the plain file to read from and for which to compute the /// delta based on base_signature /// \param[in] crc_size is the size of the crc to create if checksum is not nullptr /// \param[in] checksum if not null, the *checksum will be set to the address of a newly /// allocated crc that will receive the calculated crc of the below object, this /// CRC is calcuated for the data of "below". Caller has the duty to release this object /// when no more needed but never before this generic_rsync object has been destroyed. generic_rsync(generic_file *base_signature, generic_file *below, const infinint & crc_size, const crc **checksum); /// constructor for "patch" operation /// in this mode the generic_rsync object is read only, the data read from /// it is built from the current file's data and the delta signature. /// \param[in] current_data is a read_only object that contains the data to be used /// as base for the patch (this data is not modified) /// \param[in] delta is read only and contains the patch to apply generic_rsync(generic_file *current_data, generic_file *delta); generic_rsync(const generic_rsync & ref) = delete; generic_rsync(generic_rsync && ref) noexcept = delete; generic_rsync & operator = (const generic_rsync & ref) = delete; generic_rsync & operator = (generic_rsync && ref) noexcept = delete; ~generic_rsync() noexcept(false); // inherited from generic_file virtual bool skippable(skippability direction, const infinint & amount) override { return false; }; virtual bool skip(const infinint & pos) override {if(pos != 0 || !initial) throw SRC_BUG; else return true; }; virtual bool skip_to_eof() override { throw SRC_BUG; }; virtual bool skip_relative(S_I x) override { if(x != 0) throw SRC_BUG; else return true; }; virtual bool truncatable(const infinint & pos) const override { return pos == get_position(); }; virtual infinint get_position() const override { return x_below->get_position(); }; protected: virtual void inherited_read_ahead(const infinint & amount) override {}; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override { if(pos != get_position()) throw SRC_BUG; }; virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override; private: enum { sign, delta, patch } status; generic_file *x_below; ///< underlying layer to read from / write to generic_file *x_input; generic_file *x_output; bool initial; char *working_buffer; U_I working_size; bool patching_completed; crc *data_crc; #if LIBRSYNC_AVAILABLE rs_job_t *job; rs_signature_t *sumset; /// opaque is pointer to a generic_rsync object static rs_result patch_callback(void *opaque, rs_long_t pos, size_t *len, void **buf); #endif /// feed librsync using rs_job_iter /// \param[in] buffer_in bytes of data to give to librsync /// \param[in,out] avail_in is the amount of byte available, and after the call the amount of not yet read bytes /// remaining at the beginning of the buffer_in buffer (when shift_input is set to true) or at the end of buffer_in if shift is set /// to false. /// \param[in] shift_input /// \param[out] buffer_out where to drop the data from librsync /// \param[in,out] avail_out is the size of the allocated memory pointed to by buffer_out and after the call the amount /// of byte that has been dropped to the buffer_out buffer. /// \return true if librsync returned RS_DONE else false is return if RS_BLOCKED is returned. For any other errors an exception is thrown bool step_forward(const char *buffer_in, U_I & avail_in, bool shift_input, char *buffer_out, U_I & avail_out); void free_job(); void send_eof(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/xz_module.cpp0000644000175000017500000001365214636066467014064 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_LZMA_H #include #endif } #include "xz_module.hpp" #include "int_tools.hpp" #include "tools.hpp" using namespace std; namespace libdar { xz_module::xz_module(U_I compression_level) { #if LIBLZMA_AVAILABLE if(compression_level > 9 || compression_level < 1) throw Erange("xz_module::xz_module", tools_printf(gettext("out of range XZ compression level: %d"), compression_level)); setup(compression_level); #else throw Ecompilation(gettext("xz/lzma compression")); #endif } U_I xz_module::get_max_compressing_size() const { #if LIBLZMA_AVAILABLE U_I unused = 0; return int_tools_maxof_aggregate(unused); #else throw Ecompilation(gettext("xz/lzma compression")); #endif } U_I xz_module::get_min_size_to_compress(U_I clear_size) const { #if LIBLZMA_AVAILABLE if(clear_size > get_max_compressing_size() || clear_size < 1) throw Erange("xz_module::get_min_size_to_compress", gettext("out of range block size submitted to xz_module::get_min_size_to_compress")); return clear_size * 2; // should be large enough, liblzma does not seem // to provide mean to upper bound compressed data // given a clear data size #else throw Ecompilation(gettext("xz/lzma compression")); #endif } U_I xz_module::compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const { #if LIBLZMA_AVAILABLE U_I ret; init_compr(); lzma_str.next_out = (uint8_t*)zip_buf; lzma_str.avail_out = zip_buf_size; lzma_str.next_in = (uint8_t*)normal; lzma_str.avail_in = normal_size; switch(lzma_code(& lzma_str, LZMA_FINISH)) { case LZMA_OK: case LZMA_STREAM_END: break; // normal end case LZMA_DATA_ERROR: throw Edata(gettext("corrupted compressed data met")); case LZMA_BUF_ERROR: if(lzma_str.next_out == (uint8_t*)(zip_buf + zip_buf_size)) throw SRC_BUG; else throw Edata(gettext("corrupted compressed data met")); default: throw SRC_BUG; } ret = (char*)lzma_str.next_out - zip_buf; if(ret == zip_buf_size) throw SRC_BUG; // compressed data does not completely hold in buffer size end_process(); return ret; #else throw Ecompilation(gettext("xz/lzma compression")); #endif } U_I xz_module::uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const { #if LIBLZMA_AVAILABLE U_I ret; init_decompr(); lzma_str.next_in = (uint8_t*)zip_buf; lzma_str.avail_in = zip_buf_size; lzma_str.next_out = (uint8_t*)normal; lzma_str.avail_out = normal_size; switch(lzma_code(& lzma_str, LZMA_FINISH)) { case LZMA_OK: case LZMA_STREAM_END: break; // normal end case LZMA_DATA_ERROR: throw Edata(gettext("corrupted compressed data met")); case LZMA_BUF_ERROR: throw SRC_BUG; default: throw SRC_BUG; } ret = (char*)lzma_str.next_out - normal; end_process(); return ret; #else throw Ecompilation(gettext("xz/lzma compression")); #endif } unique_ptr xz_module::clone() const { #if LIBLZMA_AVAILABLE try { return std::make_unique(*this); } catch(bad_alloc &) { throw Ememory("xz_module::clone"); } #else throw Ecompilation(gettext("xz/lzma compression")); #endif } void xz_module::setup(U_I compression_level) { #if LIBLZMA_AVAILABLE level = compression_level; lzma_str = LZMA_STREAM_INIT; #else throw Ecompilation(gettext("lz4 compression")); #endif } void xz_module::init_decompr() const { #if LIBLZMA_AVAILABLE switch(lzma_auto_decoder(& lzma_str, UINT64_MAX, 0)) { case LZMA_OK: break; case LZMA_MEM_ERROR: throw Ememory("xz_module::init_decompr"); case LZMA_OPTIONS_ERROR: throw Ecompilation("The expected compression preset is not supported by this build of liblzma"); case LZMA_PROG_ERROR: throw SRC_BUG; // One or more of the parameters have values that will never be valid default: throw SRC_BUG; } #else throw Ecompilation(gettext("lz4 compression")); #endif } void xz_module::init_compr() const { #if LIBLZMA_AVAILABLE switch(lzma_easy_encoder(& lzma_str, level, LZMA_CHECK_CRC32)) { case LZMA_OK: break; case LZMA_MEM_ERROR: throw Ememory("xz_module::init_decompr"); case LZMA_OPTIONS_ERROR: throw Ecompilation("The given compression preset is not supported by this build of liblzma"); case LZMA_UNSUPPORTED_CHECK: throw Ecompilation("The requested check is not supported by this liblzma build"); case LZMA_PROG_ERROR: throw SRC_BUG; // One or more of the parameters have values that will never be valid default: throw SRC_BUG; // undocumented possible return code } #else throw Ecompilation(gettext("lz4 compression")); #endif } void xz_module::end_process() const { #if LIBLZMA_AVAILABLE lzma_end(& lzma_str); #else throw Ecompilation(gettext("lz4 compression")); #endif } } // end of namespace dar-2.7.15/src/libdar/mycurl_protocol.cpp0000644000175000017500000000304714636066467015307 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #ifdef HAVE_STRING_H #include #endif #include "tools.hpp" #include "mycurl_protocol.hpp" using namespace std; namespace libdar { mycurl_protocol string_to_mycurl_protocol(const std::string & arg) { mycurl_protocol ret; if(strcasecmp(arg.c_str(), "ftp") == 0) ret = proto_ftp; else if(strcasecmp(arg.c_str(), "sftp") == 0) ret = proto_sftp; else throw Erange("entrepot_libcurl::string_to_curlprotocol", tools_printf(gettext("Unknown protocol: %S"), &arg)); return ret; } } // end of namespace dar-2.7.15/src/libdar/filesystem_hard_link_read.cpp0000644000175000017500000003271414636066467017250 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif #if HAVE_TIME_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_SYS_UN_H #include #endif #if HAVE_UNISTD_H #include #endif #if STDC_HEADERS #include #endif #ifdef LIBDAR_NODUMP_FEATURE #if HAVE_SYS_IOCTL_H #include #endif #if LIBDAR_NODUMP_FEATURE == NODUMP_LINUX #include #else #if LIBDAR_NODUMP_FEATURE == NODUMP_EXT2FS #include #else #error "unknown location of ext2_fs.h include file" #endif #endif #endif #if MAJOR_IN_MKDEV #include #if !defined(makedev) && defined(mkdev) #define makedev(a,b) mkdev((a),(b)) #endif #else #if MAJOR_IN_SYSMACROS #include #endif #endif } // end extern "C" #include #include #include "filesystem_hard_link_read.hpp" #include "tools.hpp" #include "erreurs.hpp" #include "user_interaction.hpp" #include "cat_all_entrees.hpp" #include "ea_filesystem.hpp" #include "cygwin_adapt.hpp" #include "fichier_local.hpp" #include "generic_rsync.hpp" #include "null_file.hpp" using namespace std; namespace libdar { static void attach_ea(const string &chemin, cat_inode *ino, const mask & ea_mask); cat_nomme *filesystem_hard_link_read::make_read_entree(const path & lieu, const string & name, bool see_hard_link, const mask & ea_mask) { const string display = name.empty() ? lieu.display() : (lieu.append(name)).display(); const char *ptr_name = display.c_str(); cat_nomme *ref = nullptr; struct stat buf; string tmp; try { int val = 0; bool use_stat = ignore_if_symlink(display); if(use_stat) val = stat(ptr_name, &buf); else val = lstat(ptr_name, &buf); if(val < 0) { switch(errno) { case EACCES: tmp = tools_strerror_r(errno); get_ui().message(tools_printf(gettext("Error reading inode of file %s : %s"), ptr_name, tmp.c_str())); break; case ENOENT: if(display.size() >= PATH_MAX || name.size() >= NAME_MAX) get_ui().message(tools_printf(gettext("Failed reading inode information for %s: "), ptr_name) + tools_strerror_r(errno)); // Cygwin may return shorter name than expected using readdir (see class etage) which // leads the file to be truncated, and thus when we here fetch inode information // we get file non existent error. In that situation this is not the case of a // file that has been removed between the time we read the directory content and the // time here we read inode details, so we issue a warning in that situation break; default: throw Erange("filesystem_hard_link_read::make_read_entree", string(gettext("Cannot read inode for ")) + ptr_name + " : " + tools_strerror_r(errno)); } // the current method returns nullptr (= ref) (meaning file does not exists) } else { #if LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND || LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND tools_check_negative_date(buf.st_atim.tv_sec, get_ui(), ptr_name, gettext("atime, data access time"), ask_before_zeroing_neg_dates, false); // not silent tools_check_negative_date(buf.st_mtim.tv_sec, get_ui(), ptr_name, gettext("mtime, data modification time"), ask_before_zeroing_neg_dates, false); // not silent tools_check_negative_date(buf.st_ctim.tv_sec, get_ui(), ptr_name, gettext("ctime, inode change time"), ask_before_zeroing_neg_dates, false); // not silent #if LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND /* saving some place in archive not recording nanosecond time fraction */ datetime atime = datetime(buf.st_atim.tv_sec, buf.st_atim.tv_nsec/1000, datetime::tu_microsecond); datetime mtime = datetime(buf.st_mtim.tv_sec, buf.st_mtim.tv_nsec/1000, datetime::tu_microsecond); datetime ctime = datetime(buf.st_ctim.tv_sec, buf.st_ctim.tv_nsec/1000, datetime::tu_microsecond); #else datetime atime = datetime(buf.st_atim.tv_sec, buf.st_atim.tv_nsec, datetime::tu_nanosecond); datetime mtime = datetime(buf.st_mtim.tv_sec, buf.st_mtim.tv_nsec, datetime::tu_nanosecond); datetime ctime = datetime(buf.st_ctim.tv_sec, buf.st_ctim.tv_nsec, datetime::tu_nanosecond); #endif if(atime.is_null()) // assuming an error avoids getting time that way atime = datetime(buf.st_atime, 0, datetime::tu_second); if(mtime.is_null()) // assuming an error avoids getting time that way mtime = datetime(buf.st_mtime, 0, datetime::tu_second); if(ctime.is_null()) // assuming an error avoids getting time that way ctime = datetime(buf.st_ctime, 0, datetime::tu_second); #else tools_check_negative_date(buf.st_atime, get_ui(), ptr_name, gettext("atime, data access time"), ask_before_zeroing_neg_dates, false); // not silent tools_check_negative_date(buf.st_mtime, get_ui(), ptr_name, gettext("mtime, data modification time"), ask_before_zeroing_neg_dates, false); // not silent tools_check_negative_date(buf.st_ctime, get_ui(), ptr_name, gettext("ctime, inode change time"), ask_before_zeroing_neg_dates, false); // not silent datetime atime = datetime(buf.st_atime, 0, datetime::tu_second); datetime mtime = datetime(buf.st_mtime, 0, datetime::tu_second); datetime ctime = datetime(buf.st_ctime, 0, datetime::tu_second); #endif if(S_ISLNK(buf.st_mode)) { string pointed = tools_readlink(ptr_name); ref = new (nothrow) cat_lien(buf.st_uid, buf.st_gid, buf.st_mode & 07777, atime, mtime, ctime, name, pointed, buf.st_dev); } else if(S_ISREG(buf.st_mode)) ref = new (nothrow) cat_file(buf.st_uid, buf.st_gid, buf.st_mode & 07777, atime, mtime, ctime, name, lieu, buf.st_size, buf.st_dev, furtive_read_mode); else if(S_ISDIR(buf.st_mode)) ref = new (nothrow) cat_directory(buf.st_uid, buf.st_gid, buf.st_mode & 07777, atime, mtime, ctime, name, buf.st_dev); else if(S_ISCHR(buf.st_mode)) ref = new (nothrow) cat_chardev(buf.st_uid, buf.st_gid, buf.st_mode & 07777, atime, mtime, ctime, name, major(buf.st_rdev), minor(buf.st_rdev), // makedev(major, minor) buf.st_dev); else if(S_ISBLK(buf.st_mode)) ref = new (nothrow) cat_blockdev(buf.st_uid, buf.st_gid, buf.st_mode & 07777, atime, mtime, ctime, name, major(buf.st_rdev), minor(buf.st_rdev), // makedev(major, minor) buf.st_dev); else if(S_ISFIFO(buf.st_mode)) ref = new (nothrow) cat_tube(buf.st_uid, buf.st_gid, buf.st_mode & 07777, atime, mtime, ctime, name, buf.st_dev); else if(S_ISSOCK(buf.st_mode)) ref = new (nothrow) cat_prise(buf.st_uid, buf.st_gid, buf.st_mode & 07777, atime, mtime, ctime, name, buf.st_dev); #if HAVE_DOOR else if(S_ISDOOR(buf.st_mode)) ref = new (nothrow) cat_door(buf.st_uid, buf.st_gid, buf.st_mode & 07777, atime, mtime, ctime, name, lieu, buf.st_dev); #endif else throw Edata(string(gettext("Unknown file type! file name is: ")) + string(ptr_name)); cat_inode *ino = dynamic_cast(ref); if(ino != nullptr) { // // Extended Attributes Considerations // try { attach_ea(ptr_name, ino, ea_mask); } catch(Ebug & e) { throw; } catch(Euser_abort & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Ememory & e) { throw; } catch(Egeneric & ex) { get_ui().message(string(gettext("Error reading EA for "))+ptr_name+ " : " + ex.get_message()); // no throw ! // we must be able to continue without EA } // // Filesystem Specific Attributes Considerations // filesystem_specific_attribute_list *fsal = new (nothrow) filesystem_specific_attribute_list(); if(fsal == nullptr) throw Ememory("filesystem_hard_link_read::make_entree"); try { fsal->get_fsa_from_filesystem_for(get_ui(), display, sc, buf.st_mode, get_ask_before_zeroing_neg_dates()); if(!fsal->empty()) { ino->fsa_set_saved_status(fsa_saved_status::full); ino->fsa_attach(fsal); fsal = nullptr; // now managed by *ino } else { ino->fsa_set_saved_status(fsa_saved_status::none); delete fsal; fsal = nullptr; } } catch(...) { if(fsal != nullptr) delete fsal; throw; } } // // hard link detection // if(ref == nullptr) throw Ememory("filesystem_hard_link_read::make_read_entree"); if(buf.st_nlink > 1 && see_hard_link && dynamic_cast(ref) == nullptr) { map::iterator it = corres_read.find(node(buf.st_ino, buf.st_dev)); if(it == corres_read.end()) // inode not yet seen, creating the cat_etoile object { cat_inode *ino_ref = dynamic_cast(ref); cat_etoile *tmp_et = nullptr; if(ino_ref == nullptr) throw SRC_BUG; tmp_et = new (nothrow) cat_etoile(ino_ref, etiquette_counter++); if(tmp_et == nullptr) throw Ememory("filesystem_hard_link_read::make_read_entree"); try { ref = nullptr; // the object pointed to by ref is now managed by tmp_et couple tmp = couple(tmp_et, buf.st_nlink - 1); pair p_tmp(node(buf.st_ino, buf.st_dev), tmp); corres_read.insert(p_tmp); it = corres_read.find(node(buf.st_ino,buf.st_dev)); if(it == corres_read.end()) throw SRC_BUG; // the addition of the entry to the map failed !!! else it->second.obj->get_inode()->change_name(""); // name of inode attached to an cat_etoile is not used so we don't want to waste space in this field. } catch(...) { if(tmp_et != nullptr) delete tmp_et; throw; } ref = new (nothrow) cat_mirage(name, tmp_et); } else // inode already seen creating a new cat_mirage on the given cat_etoile { // some sanity checks if(it->second.obj == nullptr) throw SRC_BUG; if(ref != nullptr) delete ref; // we don't need this just created inode as it is already attached to the cat_etoile object ref = new (nothrow) cat_mirage(name, it->second.obj); if(ref != nullptr) { it->second.count--; if(it->second.count == 0) corres_read.erase(it); // this deletes the couple entry, implying the release of memory used by the holder object, but the cat_etoile will only be destroyed once its internal counter drops to zero } } } if(ref == nullptr) throw Ememory("filesystem_hard_link_read::make_read_entree"); } } catch(...) { if(ref != nullptr) { delete ref; ref = nullptr; } throw; } return ref; } static void attach_ea(const string &chemin, cat_inode *ino, const mask & ea_mask) { ea_attributs *eat = nullptr; try { if(ino == nullptr) throw SRC_BUG; eat = ea_filesystem_read_ea(chemin, ea_mask); if(eat != nullptr) { if(eat->size() <= 0) throw SRC_BUG; ino->ea_set_saved_status(ea_saved_status::full); ino->ea_attach(eat); eat = nullptr; // allocated memory now managed by the cat_inode object } else ino->ea_set_saved_status(ea_saved_status::none); } catch(...) { if(eat != nullptr) delete eat; throw; } if(eat != nullptr) throw SRC_BUG; } } // end of namespace dar-2.7.15/src/libdar/crypto_asym.cpp0000644000175000017500000003151714636255166014423 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif } // end of extern "C" #include "tools.hpp" #include "crypto_asym.hpp" #include "integers.hpp" #include "generic_file_overlay_for_gpgme.hpp" #include "thread_cancellation.hpp" #include using namespace std; namespace libdar { #if GPGME_SUPPORT static gpgme_error_t read_passphrase(void *hook, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd); #endif void crypto_asym::set_signatories(const std::vector & signatories) { #if GPGME_SUPPORT gpgme_key_t *signatories_key = nullptr; if(signatories.empty()) { gpgme_signers_clear(context); has_signatories = false; } else { build_key_list(signatories, signatories_key, true); try { gpgme_signers_clear(context); gpgme_key_t *ptr = signatories_key; gpgme_error_t err; if(ptr == nullptr) throw SRC_BUG; // build_key_list failed while(*ptr != nullptr) { err = gpgme_signers_add(context, *ptr); switch(gpgme_err_code(err)) { case GPG_ERR_NO_ERROR: break; default: throw Erange("crypto_asym::encrypt", string(gettext("Unexpected error reported by GPGME: ")) + tools_gpgme_strerror_r(err)); } ++ptr; } } catch(...) { release_key_list(signatories_key); gpgme_signers_clear(context); has_signatories = false; throw; } release_key_list(signatories_key); has_signatories = true; } #else throw Ecompilation("Asymetric Strong encryption algorithms using GPGME"); #endif } void crypto_asym::encrypt(const vector & recipients_email, generic_file & clear, generic_file & ciphered) { #if GPGME_SUPPORT gpgme_key_t *ciphering_keys = nullptr; build_key_list(recipients_email, ciphering_keys, false); try { generic_file_overlay_for_gpgme o_clear(&clear); generic_file_overlay_for_gpgme o_ciphered(&ciphered); gpgme_error_t err; if(!has_signatories) err = gpgme_op_encrypt(context, ciphering_keys, (gpgme_encrypt_flags_t)(GPGME_ENCRYPT_NO_ENCRYPT_TO|GPGME_ENCRYPT_ALWAYS_TRUST), o_clear.get_gpgme_handle(), o_ciphered.get_gpgme_handle()); else err = gpgme_op_encrypt_sign(context, ciphering_keys, (gpgme_encrypt_flags_t)(GPGME_ENCRYPT_NO_ENCRYPT_TO|GPGME_ENCRYPT_ALWAYS_TRUST), o_clear.get_gpgme_handle(), o_ciphered.get_gpgme_handle()); switch(gpgme_err_code(err)) { case GPG_ERR_NO_ERROR: break; case GPG_ERR_INV_VALUE: throw SRC_BUG; case GPG_ERR_UNUSABLE_PUBKEY: throw Erange("crypto_asym::encrypt", gettext("Key found but users are not all trusted")); default: throw Erange("crypto_asym::encrypt", string(gettext("Unexpected error reported by GPGME: ")) + tools_gpgme_strerror_r(err)); } } catch(...) { release_key_list(ciphering_keys); throw; } release_key_list(ciphering_keys); #else throw Ecompilation("Asymetric Strong encryption algorithms using GPGME"); #endif } void crypto_asym::decrypt(generic_file & ciphered, generic_file & clear) { #if GPGME_SUPPORT generic_file_overlay_for_gpgme o_clear(&clear); generic_file_overlay_for_gpgme o_ciphered(&ciphered); gpgme_error_t err = gpgme_op_decrypt_verify(context, o_ciphered.get_gpgme_handle(), o_clear.get_gpgme_handle()); signing_result.clear(); switch(gpgme_err_code(err)) { case GPG_ERR_NO_ERROR: fill_signing_result(); break; case GPG_ERR_INV_VALUE: throw SRC_BUG; case GPG_ERR_NO_DATA: throw Erange("crypto_asym::decrypt", gettext("No data to decrypt")); case GPG_ERR_DECRYPT_FAILED: throw Erange("crypto_asym::decrypt", gettext("Invalid Cipher text")); case GPG_ERR_BAD_PASSPHRASE: throw Erange("crypto_asym::decrypt", gettext("Failed retreiving passphrase")); default: throw Erange("crypto_asym::decrypt", string(gettext("Unexpected error reported by GPGME: ")) + tools_gpgme_strerror_r(err)); } #else throw Ecompilation("Asymetric Strong encryption algorithms using GPGME"); #endif } void crypto_asym::build_context() { #if GPGME_SUPPORT gpgme_error_t err = gpgme_new(&context); if(gpgme_err_code(err) != GPG_ERR_NO_ERROR) throw Erange("crypto_asym::crypto_asym", string(gettext("Failed creating GPGME context: ")) + tools_gpgme_strerror_r(err)); err = gpgme_set_protocol(context, GPGME_PROTOCOL_OpenPGP); if(gpgme_err_code(err) != GPG_ERR_NO_ERROR) throw Erange("crypto_asym::crypto_asym", string(gettext("Failed setting GPGME context with OpenPGP protocol: ")) + tools_gpgme_strerror_r(err)); gpgme_set_passphrase_cb(context, read_passphrase, (void *)this); #endif } #if GPGME_SUPPORT void crypto_asym::build_key_list(const vector & recipients_email, gpgme_key_t * & ciphering_keys, bool signatories) { U_I size = recipients_email.size() + 1; ciphering_keys = new (nothrow) gpgme_key_t[size]; if(ciphering_keys == nullptr) throw Ememory("crypto_asym::build_key_list"); // clearing all fields in order to be able to know which // index has been allocated and need to be restored in case of error for(U_I i = 0; i < size ; ++i) ciphering_keys[i] = nullptr; try { gpgme_error_t err = GPG_ERR_NO_ERROR; gpgme_user_id_t id = nullptr; gpgme_subkey_t subk = nullptr; bool found = false; bool eof = false; bool search_email = false; U_I offset = 0; // for each recipient, listing all keys to find a match for(U_I i = 0; i < recipients_email.size(); ++i) { err = gpgme_op_keylist_start(context, nullptr, 0); switch(gpgme_err_code(err)) { case GPG_ERR_NO_ERROR: break; case GPG_ERR_INV_VALUE: throw SRC_BUG; default: throw Erange("crypto_asym::decrypt", string(gettext("Unexpected error reported by GPGME: ")) + tools_gpgme_strerror_r(err)); } // the the recipient string contains an '@' we assume we have // to look for an email // else we assume we have to look for an keyid search_email = recipients_email[i].find_first_of('@') != string::npos; found = false; eof = false; do { // getting the next key err = gpgme_op_keylist_next(context, &(ciphering_keys[i - offset])); switch(gpgme_err_code(err)) { case GPG_ERR_EOF: eof = true; break; case GPG_ERR_NO_ERROR: if(search_email) { // looking in email field id = ciphering_keys[i - offset]->uids; while(!found && id != nullptr) { // for each key, listing all emails associated with it to find a match found = (strncmp(recipients_email[i].c_str(), id->email, recipients_email[i].size()) == 0) && (id->email[recipients_email[i].size()] == '\0'); id = id->next; } } else { // looking in keyid field of all subkeys subk = ciphering_keys[i - offset]->subkeys; while(!found && subk != nullptr) { // for each key, listing all keyid associated with it to find a match found = (strncmp(recipients_email[i].c_str(), subk->keyid, recipients_email[i].size()) == 0) && (subk->keyid[recipients_email[i].size()] == '\0'); subk = subk->next; } } // analysing search result if(found) { if(ciphering_keys[i - offset]->revoked || ciphering_keys[i - offset]->expired || ciphering_keys[i - offset]->disabled || ciphering_keys[i - offset]->invalid) found = false; if(signatories) { if(!ciphering_keys[i - offset]->can_sign) found = false; } else { if(!ciphering_keys[i - offset]->can_encrypt) found = false; } } if(!found) { gpgme_key_unref(ciphering_keys[i - offset]); ciphering_keys[i - offset] = nullptr; } break; default: throw Erange("crypto_asym::decrypt", string(gettext("Unexpected error reported by GPGME: ")) + tools_gpgme_strerror_r(err)); } } while(!found && !eof); // if we exit before gpgme_op_keylist_next() return GPG_ERR_EOF // we must update the state of the context to end the key listing operation if(!eof) (void)gpgme_op_keylist_end(context); if(!found) { if(signatories) get_ui().printf(gettext("No valid signing key could be found for %S"), &(recipients_email[i])); else get_ui().printf(gettext("No valid encryption key could be found for %S"), &(recipients_email[i])); get_ui().pause("Do you want to continue without this recipient?"); ++offset; } } // if no recipient could be found at all aborting the operation if(offset + 1 >= size) { if(signatories) throw Erange("crypto_asym::build_key_list", gettext("No signatory remain with a valid key, signing is impossible, aborting")); else throw Erange("crypto_asym::build_key_list", gettext("No recipient remain with a valid key, encryption is impossible, aborting")); } // the key list must end wth a nullptr entry if(ciphering_keys[size - 1 - offset] != nullptr) throw SRC_BUG; } catch(...) { release_key_list(ciphering_keys); throw; } } void crypto_asym::release_key_list(gpgme_key_t * & ciphering_keys) { if(ciphering_keys != nullptr) { for(U_I i = 0; ciphering_keys[i] != nullptr; ++i) gpgme_key_unref(ciphering_keys[i]); delete [] ciphering_keys; ciphering_keys = nullptr; } } void crypto_asym::fill_signing_result() { gpgme_verify_result_t inter = gpgme_op_verify_result(context); gpgme_signature_t res = nullptr; signator tmp; signing_result.clear(); if(inter != nullptr) res = inter->signatures; else res = nullptr; while(res != nullptr) { if(res->summary & (GPGME_SIGSUM_VALID|GPGME_SIGSUM_GREEN)) tmp.result = signator::good; else if(res->summary & GPGME_SIGSUM_RED) tmp.result = signator::bad; else if(res->summary & GPGME_SIGSUM_KEY_MISSING) tmp.result = signator::unknown_key; else tmp.result = signator::error; if(res->summary & GPGME_SIGSUM_KEY_REVOKED) tmp.key_validity = signator::revoked; else if(res->summary & GPGME_SIGSUM_KEY_EXPIRED) tmp.key_validity = signator::expired; else tmp.key_validity = signator::valid; tmp.fingerprint = res->fpr; tmp.signing_date = datetime(res->timestamp); tmp.signature_expiration_date = datetime(res->exp_timestamp); res = res->next; signing_result.push_back(tmp); } signing_result.sort(); } static gpgme_error_t read_passphrase(void *hook, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd) { crypto_asym *obj = (crypto_asym *)(hook); const char * precision = gettext("Passphrase required for key %s :"); string message; secu_string pass; ssize_t wrote; gpgme_error_t ret = GPG_ERR_NO_ERROR; thread_cancellation th; if(obj == nullptr) throw SRC_BUG; if(uid_hint != nullptr) message = tools_printf(precision, uid_hint); else { if(passphrase_info != nullptr) message = tools_printf(precision, passphrase_info); else message = tools_printf(precision, ""); } if(prev_was_bad) obj->get_ui().message(gettext("Error, invalid passphrase given, try again:")); pass = obj->get_ui().get_secu_string(message, false); th.check_self_cancellation(); wrote = write(fd, pass.c_str(), pass.get_size()); if(wrote < 0 || (U_I)(wrote) != pass.get_size()) { if(wrote == -1) obj->get_ui().message(string(gettext("Error, while sending the passphrase to GPGME:")) + tools_strerror_r(errno)); else obj->get_ui().message(gettext("Failed sending the totality of the passphrase to GPGME")); ret = GPG_ERR_CANCELED; } else { wrote = write(fd, "\n", 1); if(wrote != 1) obj->get_ui().message(gettext("Failed sending CR after the passphrase")); ret = GPG_ERR_NO_ERROR; } return ret; } #endif } // end of namespace dar-2.7.15/src/libdar/tlv.cpp0000644000175000017500000000332714636066467012661 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_NETINET_IN_H #include #endif } #include "tlv.hpp" using namespace std; namespace libdar { void tlv::dump(generic_file & f) const { U_16 tmp; tlv *me = const_cast(this); if(me == nullptr) throw SRC_BUG; tmp = htons(type); f.write((char *)&tmp, 2); size().dump(f); me->skip(0); me->copy_to(f); } void tlv::setup(generic_file & f) { init(f); } void tlv::init(generic_file & f) { infinint length; f.read((char *)&type, 2); type = ntohs(type); length.read(f); reset(); if(f.copy_to(*this, length) != length) throw Erange("tlv::init",gettext("Missing data to initiate a TLV object")); } } // end of namespace dar-2.7.15/src/libdar/int_tools.cpp0000644000175000017500000000410314636066467014057 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "int_tools.hpp" #include "erreurs.hpp" namespace libdar { void int_tools_swap_bytes(unsigned char &a, unsigned char &b) { unsigned char c = a; a = b; b = c; } void int_tools_swap_bytes(unsigned char *a, U_I size) { if(size <= 1) return; else { int_tools_swap_bytes(a[0], a[size-1]); int_tools_swap_bytes(a+1, size-2); // terminal recursivity } } void int_tools_expand_byte(unsigned char a, int_tools_bitfield &bit) { unsigned char mask = 0x80; for(S_I i = 0; i < 8; ++i) { bit[i] = (a & mask) >> (7 - i); mask >>= 1; } } void int_tools_contract_byte(const int_tools_bitfield &b, unsigned char & a) { a = 0; for(S_I i = 0; i < 8; ++i) { a <<= 1; if(b[i] > 1) throw Erange("infinint.cpp : contract_byte", gettext("a binary digit is either 0 or 1")); a += b[i]; } } } // end of namespace dar-2.7.15/src/libdar/secu_string.cpp0000644000175000017500000001645314636066467014405 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_GCRYPT_H #ifndef GCRYPT_NO_DEPRECATED #define GCRYPT_NO_DEPRECATED #endif #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif } #include "erreurs.hpp" #include "secu_string.hpp" #include "tools.hpp" using namespace std; namespace libdar { bool secu_string::is_string_secured() { #if CRYPTO_AVAILABLE return true; #else return false; #endif } void secu_string::set(int fd, U_I size) { U_I offset = 0; S_I lu; if(size < get_allocated_size()) { clean_and_destroy(); init(size); } else clear(); do { lu = ::read(fd, mem + offset, *allocated_size - 1 - offset); if(lu < 0) { *string_size = offset; mem[offset] = '\0'; throw Erange("secu_string::read", string(gettext("Error while reading data for a secure memory:" )) + tools_strerror_r(errno)); } else offset += lu; } while(lu > 0 && offset < size); *string_size = offset; if(*string_size >= *allocated_size) throw SRC_BUG; else mem[*string_size] = '\0'; } void secu_string::append_at(U_I offset, const char *ptr, U_I size) { if(offset > *string_size) throw Erange("secu_string::append", gettext("appending data over secure_memory its end")); if(size + offset >= *allocated_size) throw Esecu_memory("secu_string::append"); (void)memcpy(mem + offset, ptr, size); offset += size; *string_size = offset; mem[*string_size] = '\0'; } void secu_string::append_at(U_I offset, int fd, U_I size) { if(offset > *string_size) throw Erange("secu_string::append", gettext("appending data after the end of a secure_memory")); if(size + offset >= *allocated_size) throw Erange("secu_string::append", gettext("Cannot receive that much data in regard to the allocated memory")); S_I lu = ::read(fd, mem + offset, size); if(lu < 0) { mem[*string_size] = '\0'; throw Erange("secu_string::read", string(gettext("Error while reading data for a secure memory:" )) + tools_strerror_r(errno)); } if(lu + offset >= *allocated_size) throw SRC_BUG; offset += lu; if(*string_size < offset) *string_size = offset; mem[*string_size] = '\0'; } void secu_string::reduce_string_size_to(U_I pos) { if(pos > *string_size) throw Erange("secu_string::reduce_string_size_to", gettext("Cannot reduce the string to a size that is larger than its current size")); *string_size = pos; mem[*string_size] = '\0'; } void secu_string::expand_string_size_to(U_I size) { if(size > get_allocated_size()) throw Erange("secu_string::expand_string_size_to", gettext("Cannot expand secu_string size past its allocation size")); if(size < *string_size) throw Erange("secu_stering::expand_string_size_to", gettext("Cannot shrink a secu_string")); memset(mem + *string_size, '\0', size - *string_size); *string_size = size; } void secu_string::randomize(U_I size) { #if CRYPTO_AVAILABLE set_size(size); gcry_randomize(mem, *string_size, GCRY_STRONG_RANDOM); #else throw Efeature("string randomization lacks libgcrypt"); #endif } void secu_string::set_size(U_I size) { if(size > get_allocated_size()) throw Erange("secu_string::set_size", gettext("exceeding storage capacity while requesting secu_string::set_size()")); *string_size = size; } char & secu_string::operator[] (U_I index) { if(index < get_size()) return mem[index]; else throw Erange("secu_string::operator[]", gettext("Out of range index requested for a secu_string")); } void secu_string::init(U_I size) { nullifyptr(); try { #if CRYPTO_AVAILABLE allocated_size = (U_I *)gcry_malloc_secure(sizeof(U_I)); if(allocated_size == nullptr) throw Esecu_memory("secu_string::secus_string"); #else allocated_size = new (nothrow) U_I; if(allocated_size == nullptr) throw Ememory("secu_string::secus_string"); #endif *allocated_size = size + 1; #if CRYPTO_AVAILABLE mem = (char *)gcry_malloc_secure(*allocated_size); if(mem == nullptr) throw Esecu_memory("secu_string::secus_string"); #else mem = new (nothrow) char[*allocated_size]; if(mem == nullptr) throw Ememory("secu_string::secus_string"); #endif #if CRYPTO_AVAILABLE string_size = (U_I *)gcry_malloc_secure(sizeof(U_I)); if(string_size == nullptr) throw Esecu_memory("secu_string::secus_string"); #else string_size = new (nothrow) U_I; if(string_size == nullptr) throw Ememory("secu_string::secus_string"); #endif *string_size = 0; mem[0] = '\0'; } catch(...) { clean_and_destroy(); throw; } } void secu_string::copy_from(const secu_string & ref) { if(ref.allocated_size == nullptr) throw SRC_BUG; if(*(ref.allocated_size) == 0) throw SRC_BUG; if(ref.mem == nullptr) throw SRC_BUG; if(ref.string_size == nullptr) throw SRC_BUG; init(*(ref.allocated_size) - 1); (void)memcpy(mem, ref.mem, *(ref.string_size) + 1); // +1 to copy the ending '\0' *string_size = *(ref.string_size); } bool secu_string::compare_with(const char *ptr, U_I size) const { if(size != *string_size) return false; else { U_I cur = 0; while(cur < size && ptr[cur] == mem[cur]) ++cur; return cur == size; } } void secu_string::clean_and_destroy() { if(string_size != nullptr) { (void)memset(string_size, 0, sizeof(U_I)); #if CRYPTO_AVAILABLE gcry_free(string_size); #else delete string_size; #endif string_size = nullptr; } if(mem != nullptr) { if(allocated_size != nullptr) (void)memset(mem, 0, *allocated_size); #if CRYPTO_AVAILABLE gcry_free(mem); #else delete [] mem; #endif mem = nullptr; } if(allocated_size != nullptr) { (void)memset(allocated_size, 0, sizeof(U_I)); #if CRYPTO_AVAILABLE gcry_free(allocated_size); #else delete allocated_size; #endif allocated_size = nullptr; } } } // end of namespace dar-2.7.15/src/libdar/i_entrepot_libcurl.hpp0000644000175000017500000001336714636067146015745 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file i_entrepot_libcurl.hpp /// \brief defines the implementation for remote filesystem entrepot using libcurl /// \ingroup Private #ifndef I_ENTREPOT_LIBCURL_HPP #define I_ENTREPOT_LIBCURL_HPP #include "../my_config.h" extern "C" { #if LIBCURL_AVAILABLE #if HAVE_CURL_CURL_H #include #endif #endif } #include #include #include "entrepot_libcurl.hpp" #include "secu_string.hpp" #include "mem_ui.hpp" #include "mycurl_easyhandle_sharing.hpp" namespace libdar { /// \addtogroup Private /// @{ /// implementation of libcurl_entrepot (pimpl paradigm) #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) class entrepot_libcurl::i_entrepot_libcurl : public entrepot, public mem_ui { public: i_entrepot_libcurl(const std::shared_ptr & dialog, ///< for user interaction mycurl_protocol proto, ///< network protocol to use const std::string & login, ///< user login on remote host const secu_string & password, ///< user password on remote host (empty for file auth or user interaction) const std::string & host, ///< the remote server to connect to const std::string & port, ///< TCP/UDP port to connec to (empty string for default) bool auth_from_file, ///< whether to check $HOME/.netrc for password const std::string & sftp_pub_keyfile, ///< where to fetch the public key (sftp only) const std::string & sftp_prv_keyfile, ///< where to fetch the private key (sftp only) const std::string & sftp_known_hosts, ///< location of the known_hosts file (empty string to disable this security check) U_I waiting_time, ///< time in second to wait before retrying in case of network error bool verbose ///< whether to have verbose messages from libcurl ); i_entrepot_libcurl(const i_entrepot_libcurl & ref) = default; i_entrepot_libcurl(i_entrepot_libcurl && ref) = default; i_entrepot_libcurl & operator = (const i_entrepot_libcurl & ref) = delete; i_entrepot_libcurl & operator = (i_entrepot_libcurl && ref) noexcept = delete; ~i_entrepot_libcurl() throw () {}; // inherited from class entrepot /// \note this is expected to have a double slash after the host:port /// like ftp://www.some.where:8021//tmp/sub/dir virtual std::string get_url() const override { return base_URL + get_full_path().display_without_root(); }; virtual void read_dir_reset() const override; virtual bool read_dir_next(std::string & filename) const override; virtual entrepot *clone() const override { return new (std::nothrow) i_entrepot_libcurl(*this); }; protected: // inherited from class entrepot virtual fichier_global *inherited_open(const std::shared_ptr & dialog, const std::string & filename, gf_mode mode, bool force_permission, U_I permission, bool fail_if_exists, bool erase) const override; virtual void inherited_unlink(const std::string & filename) const override; virtual void read_dir_flush() override; private: mycurl_protocol x_proto; std::string base_URL; ///< URL of the repository with only minimum path (login/password is given outside the URL) mycurl_easyhandle_sharing easyh; std::deque current_dir; std::string reading_dir_tmp; U_I wait_delay; bool verbosity; std::string get_libcurl_URL() const; void set_libcurl_authentication(user_interaction & dialog, ///< for user interaction const std::string & location, ///< server to authenticate with const std::string & login, ///< login to use const secu_string & password, ///< password (emtpy for interaction or file auth) bool auth_from_file, ///< if set, check for $HOME/.netrc for password const std::string & sftp_pub_keyfile, ///< where to fetch the public key (sftp only) const std::string & sftp_prv_keyfile, ///< where to fetch the private key (sftp only) const std::string & sftp_known_hosts ///< where to fetch the .known_hosts file (sftp only) ); void detruit(); static std::string mycurl_protocol2string(mycurl_protocol proto); static std::string build_url_from(mycurl_protocol proto, const std::string & host, const std::string & port); static size_t get_ftp_listing_callback(void *buffer, size_t size, size_t nmemb, void *userp); static size_t null_callback(void *buffer, size_t size, size_t nmemb, void *userp) { return size*nmemb; }; static bool mycurl_is_protocol_available(mycurl_protocol proto); // needed to access the entrepot inherited protected methods friend class entrepot_libcurl; }; #endif /// @} } // end of namespace #endif dar-2.7.15/src/libdar/criterium.cpp0000644000175000017500000004043014636066467014053 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "criterium.hpp" #include "nls_swap.hpp" #include "cat_all_entrees.hpp" #include "tools.hpp" #include "cat_nomme.hpp" #include "cat_inode.hpp" #include "cat_directory.hpp" using namespace std; namespace libdar { static const cat_inode *get_inode(const cat_nomme * arg); ///////////////////////////////////////////////////////////////////// //////////// implementation of criterium classes follows //////////// ///////////////////////////////////////////////////////////////////// bool crit_in_place_is_inode::evaluate(const cat_nomme &first, const cat_nomme &second) const { return dynamic_cast(&first) != nullptr || dynamic_cast(&first) != nullptr; } bool crit_in_place_is_dir::evaluate(const cat_nomme &first, const cat_nomme &second) const { return dynamic_cast(&first) != nullptr; } bool crit_in_place_is_file::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); return dynamic_cast(first_i) != nullptr && dynamic_cast(first_i) == nullptr; } bool crit_in_place_is_hardlinked_inode::evaluate(const cat_nomme &first, const cat_nomme &second) const { return dynamic_cast(&first) != nullptr; } bool crit_in_place_is_new_hardlinked_inode::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_mirage * tmp = dynamic_cast(&first); return tmp != nullptr && tmp->is_first_mirage(); } bool crit_in_place_data_more_recent::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_inode *second_i = get_inode(&second); datetime first_date = first_i != nullptr ? first_i->get_last_modif() : datetime(0); datetime second_date = second_i != nullptr ? second_i->get_last_modif() : datetime(0); return first_i == nullptr || first_date >= second_date || tools_is_equal_with_hourshift(x_hourshift, first_date, second_date); } bool crit_in_place_data_more_recent_or_equal_to::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); datetime first_date = first_i != nullptr ? first_i->get_last_modif() : datetime(0); return first_i == nullptr || first_date >= x_date || tools_is_equal_with_hourshift(x_hourshift, first_date, x_date); } bool crit_in_place_data_bigger::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_inode *second_i = get_inode(&second); const cat_file *first_f = dynamic_cast(first_i); const cat_file *second_f = dynamic_cast(second_i); if(first_f != nullptr && second_f != nullptr) return first_f->get_size() >= second_f->get_size(); else return true; } bool crit_in_place_data_saved::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); if(first_i != nullptr) return first_i->get_saved_status() == saved_status::saved; else return true; } bool crit_in_place_data_dirty::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_file *first_f = dynamic_cast(first_i); if(first_f != nullptr) return first_f->is_dirty(); else return false; } bool crit_in_place_data_sparse::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_file *first_f = dynamic_cast(first_i); if(first_f != nullptr) return first_f->get_sparse_file_detection_read(); else return false; } bool crit_in_place_has_delta_sig::evaluate(const cat_nomme & first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_file *first_f = dynamic_cast(first_i); if(first_f != nullptr) return first_f->has_delta_signature_available(); else return false; } bool crit_same_inode_data::evaluate(const cat_nomme &first, const cat_nomme &second) const { crit_same_type same_type; crit_in_place_is_inode is_inode; if(! same_type.evaluate(first, second)) return false; if(! is_inode.evaluate(first, second)) return false; else { ///// // inode level consideration const cat_inode* first_i = get_inode(&first); const cat_inode* second_i = get_inode(&second); if(first_i == nullptr || second_i == nullptr) throw SRC_BUG; // show be both inodes if(first_i->get_uid() != second_i->get_uid()) return false; if(first_i->get_gid() != second_i->get_gid()) return false; if(first_i->get_perm() != second_i->get_perm()) return false; if(first_i->get_last_modif() != second_i->get_last_modif()) return false; ///// // plain file specific considerations const cat_file* first_f = dynamic_cast(first_i); const cat_file* second_f = dynamic_cast(second_i); if(first_f != nullptr) { if(second_f == nullptr) throw SRC_BUG; // they should be of the same type if(first_f->get_size() != second_f->get_size()) return false; } ///// // devices specific considerations const cat_device* first_d = dynamic_cast(first_i); const cat_device* second_d = dynamic_cast(second_i); if(first_d != nullptr && first_d->get_saved_status() == saved_status::saved) { if(second_d == nullptr) throw SRC_BUG; // they should be of the same type if(second_d->get_saved_status() != saved_status::saved) return true; // cannot compare any further if(first_d->get_major() != second_d->get_major()) return false; if(first_d->get_minor() != second_d->get_minor()) return false; } ///// // symlink specific considerations const cat_lien* first_l = dynamic_cast(first_i); const cat_lien* second_l = dynamic_cast(second_i); if(first_l != nullptr && first_l->get_saved_status() == saved_status::saved) { if(second_l == nullptr) throw SRC_BUG; if(second_l->get_saved_status() != saved_status::saved) return true; // cannot comparer any further if(first_l->get_target() != second_l->get_target()) return false; } return true; // no difference was met so far } } bool crit_in_place_EA_present::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *tmp = dynamic_cast(&first); return tmp != nullptr && tmp->ea_get_saved_status() != ea_saved_status::none && tmp->ea_get_saved_status() != ea_saved_status::removed; } bool crit_in_place_EA_more_recent::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_inode *second_i = get_inode(&second); datetime ctime_f, ctime_s; if(first_i != nullptr) { switch(first_i->ea_get_saved_status()) { case ea_saved_status::none: case ea_saved_status::removed: ctime_f = datetime(0); break; default: ctime_f = first_i->get_last_change(); } } else ctime_f = datetime(0); if(second_i != nullptr) { switch(second_i->ea_get_saved_status()) { case ea_saved_status::none: case ea_saved_status::removed: return true; break; default: ctime_s = second_i->get_last_change(); } } else return true; return ctime_f >= ctime_s || tools_is_equal_with_hourshift(x_hourshift, ctime_f, ctime_s); } bool crit_in_place_EA_more_recent_or_equal_to::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); datetime ctime_f; if(first_i != nullptr) { switch(first_i->ea_get_saved_status()) { case ea_saved_status::none: case ea_saved_status::removed: ctime_f = datetime(0); break; default: ctime_f = first_i->get_last_change(); } } else ctime_f = datetime(0); return ctime_f >= x_date || tools_is_equal_with_hourshift(x_hourshift, ctime_f, x_date); } bool crit_in_place_more_EA::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_inode *second_i = get_inode(&second); infinint first_nEA, second_nEA; if(first_i != nullptr) { switch(first_i->ea_get_saved_status()) { case ea_saved_status::full: first_nEA = first_i->get_ea()->size(); break; default: first_nEA = 0; } } else first_nEA = 0; if(second_i != nullptr) { switch(second_i->ea_get_saved_status()) { case ea_saved_status::full: second_nEA = second_i->get_ea()->size(); break; default: second_nEA = 0; } } else second_nEA = 0; return first_nEA >= second_nEA; } bool crit_in_place_EA_bigger::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_inode *second_i = get_inode(&second); infinint first_EA_size, second_EA_size; if(first_i != nullptr) { switch(first_i->ea_get_saved_status()) { case ea_saved_status::full: first_EA_size = first_i->get_ea()->space_used(); break; default: first_EA_size = 0; } } else first_EA_size = 0; if(second_i != nullptr) { switch(second_i->ea_get_saved_status()) { case ea_saved_status::full: second_EA_size = second_i->get_ea()->space_used(); break; default: second_EA_size = 0; } } else second_EA_size = 0; return first_EA_size >= second_EA_size; } bool crit_in_place_EA_saved::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); return first_i != nullptr && first_i->ea_get_saved_status() == ea_saved_status::full; } bool crit_same_type::evaluate(const cat_nomme &first, const cat_nomme &second) const { const cat_inode *first_i = get_inode(&first); const cat_inode *second_i = get_inode(&second); const cat_file * first_file = dynamic_cast(first_i); const cat_lien * first_lien = dynamic_cast(first_i); const cat_directory * first_dir = dynamic_cast(first_i); const cat_chardev * first_char = dynamic_cast(first_i); const cat_blockdev * first_block = dynamic_cast(first_i); const cat_tube * first_tube = dynamic_cast(first_i); const cat_prise * first_prise = dynamic_cast(first_i); const cat_detruit *first_detruit = dynamic_cast(&first); // first not first_i here ! const cat_file * second_file = dynamic_cast(second_i); const cat_lien * second_lien = dynamic_cast(second_i); const cat_directory * second_dir = dynamic_cast(second_i); const cat_chardev * second_char = dynamic_cast(second_i); const cat_blockdev * second_block = dynamic_cast(second_i); const cat_tube * second_tube = dynamic_cast(second_i); const cat_prise * second_prise = dynamic_cast(second_i); const cat_detruit *second_detruit = dynamic_cast(&second); // second not second_i here ! return (first_file != nullptr && second_file != nullptr) || (first_lien != nullptr && second_lien != nullptr) || (first_dir != nullptr && second_dir != nullptr) || (first_char != nullptr && second_char != nullptr) || (first_block != nullptr && second_block != nullptr) || (first_tube != nullptr && second_tube != nullptr) || (first_prise != nullptr && second_prise != nullptr) || (first_detruit != nullptr && second_detruit != nullptr); } void crit_not::copy_from(const crit_not & ref) { if(ref.x_crit == nullptr) throw SRC_BUG; if(ref.x_crit == nullptr) throw SRC_BUG; x_crit = ref.x_crit->clone(); if(x_crit == nullptr) throw Ememory("crit_not::copy_from"); } void crit_and::add_crit(const criterium & ref) { criterium *cloned = ref.clone(); if(cloned == nullptr) throw Ememory("crit_and::add_crit"); try { operand.push_back(cloned); } catch(...) { if(operand.back() == cloned) operand.pop_back(); delete cloned; throw; } } void crit_and::gobe(crit_and & to_be_voided) { deque::iterator it = to_be_voided.operand.begin(); try { while(it != to_be_voided.operand.end()) { if(*it == nullptr) throw SRC_BUG; operand.push_back(*it); ++it; } to_be_voided.operand.clear(); } catch(...) { if(it != to_be_voided.operand.end() && operand.back() == *it) ++it; while(it != to_be_voided.operand.end()) { if(*it != nullptr) { delete *it; *it = nullptr; } ++it; } to_be_voided.operand.clear(); throw; } } bool crit_and::evaluate(const cat_nomme & first, const cat_nomme & second) const { bool ret = true; NLS_SWAP_IN; try { deque::const_iterator it = operand.begin(); if(it == operand.end()) throw Erange("crit_and::evaluate", gettext("Cannot evaluate this crit_and criterium as no criterium has been added to it")); while(ret && it != operand.end()) { ret &= (*it)->evaluate(first, second); ++it; }; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } void crit_and::copy_from(const crit_and & ref) { deque::const_iterator it = ref.operand.begin(); operand.clear(); try { criterium *cloned; while(it != ref.operand.end()) { cloned = (*it)->clone(); if(cloned == nullptr) throw Ememory("crit_add::copy_from"); operand.push_back(cloned); ++it; } } catch(...) { detruit(); throw; } } void crit_and::detruit() { deque::iterator it = operand.begin(); while(it != operand.end()) { if(*it != nullptr) { delete *it; *it = nullptr; } ++it; } operand.clear(); } bool crit_or::evaluate(const cat_nomme & first, const cat_nomme & second) const { bool ret = false; NLS_SWAP_IN; try { deque::const_iterator it = operand.begin(); if(it == operand.end()) throw Erange("crit_or::evaluate", gettext("Cannot evaluate this crit_or criterium as no criterium has been added to it")); while(!ret && it != operand.end()) { ret |= (*it)->evaluate(first, second); ++it; }; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } ///////////////////////////////////////////////////////////////////// //////////// implementation of private function ///////////////////// ///////////////////////////////////////////////////////////////////// static const cat_inode *get_inode(const cat_nomme *arg) { const cat_inode *ret; const cat_mirage *arg_m = dynamic_cast(arg); if(arg_m != nullptr) ret = const_cast(arg_m->get_inode()); else ret = dynamic_cast(arg); return ret; } } // end of namespace dar-2.7.15/src/libdar/header.cpp0000644000175000017500000002611114636066467013300 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif } // end extern "C" #include "header.hpp" #include "tlv_list.hpp" #include "tools.hpp" #include "fichier_global.hpp" using namespace std; namespace libdar { enum extension_type { extension_none = 'N', //< no extension (obsolete since format "08") extension_size = 'S', //< extension is the size of slices (obsolete since format "08") extension_tlv = 'T' //< extension is a TLV (systematic since format "08") }; enum tlv_type { tlv_size = 1, //< TLV gives the size of slices (infinint) tlv_first_size = 2, //< TLV gives the size of first slice (infinint) tlv_data_name = 3, //< TLV gives the name of the data set tlv_reserved = 65535 //< TLV reserved if 16 bits type space is exhausted to signal a new (larger type storage, to be implemented of course) }; /*************************************************************/ /******************* HEADER datastructure ********************/ /*************************************************************/ header::header() { magic = 0; internal_name.clear(); data_name.clear(); flag = '\0'; first_size = nullptr; slice_size = nullptr; old_header = false; } void header::read(user_interaction & ui, generic_file & f, bool lax) { magic_number tmp; tlv_list tempo; char extension; fichier_global *f_fic = dynamic_cast(&f); free_pointers(); old_header = false; if(f.read((char *)&tmp, sizeof(magic_number)) != sizeof(magic_number)) throw Erange("header::read", gettext("Reached end of file while reading slice header")); magic = ntohl(tmp); try { internal_name.read(f); } catch(Erange & e) { throw Erange("header::read", gettext("Reached end of file while reading slice header")); } if(f.read(&flag, 1) != 1) throw Erange("header::read", gettext("Reached end of file while reading slice header")); if(f.read(&extension, 1) != 1) throw Erange("header::read", gettext("Reached end of file while reading slice header")); data_name.clear(); switch(extension) { case extension_none: if(f_fic != nullptr) { slice_size = new (nothrow) infinint(f_fic->get_size()); if(slice_size == nullptr) { if(!lax) throw Ememory("header::read"); else { ui.message(gettext("LAX MODE: slice size is not possible to read, (lack of virtual memory?), continuing anyway...")); slice_size = new (nothrow) infinint(0); if(slice_size == nullptr) throw Ememory("header::read"); } // this extension was used in archives before release 2.4.0 // when the first slice had the same size of the following ones // the slice size of all slices if thus the one of the first which // was learnt by getting the size of the file // this works also for single sliced archives. } } old_header = true; break; case extension_size: slice_size = new (nothrow) infinint(f); if(slice_size == nullptr) { if(!lax) throw Ememory("header::read"); else { ui.message(gettext("LAX MODE: slice size is not possible to read, (lack of virtual memory?), continuing anyway...")); slice_size = new (nothrow) infinint(0); if(slice_size == nullptr) throw Ememory("header::read"); } } if(f_fic != nullptr) { first_size = new (nothrow) infinint(f_fic->get_size()); if(first_size == nullptr) { if(!lax) throw Ememory("header::read"); else { ui.message(gettext("LAX MODE: first slice size is not possible to read, (lack of virtual memory?), continuing anyway...")); first_size = new (nothrow) infinint(0); if(first_size == nullptr) throw Ememory("header::read"); } // note: the "extension_size" extension was used in archives before release 2.4.0 // this option was only used in the first slice and contained the size of slices (not of the first slice) // when the first slice had a different size. This way, reading the size of the current file gives // the size of the first slice while the header extension gives the size of following slices. } } else if(!lax) throw Erange("header::read", gettext("Archive format older than \"08\" (release 2.4.0) cannot be read through a single pipe. It only can be read using dar_slave or normal plain file (slice)")); else ui.message(gettext("LAX MODE: first slice size is not possible to read, continuing anyway...")); old_header = true; break; case extension_tlv: tempo.read(f); // read the list of TLV stored in the header fill_from(ui, tempo); // from the TLV list, set the different fields of the current header object if(slice_size == nullptr && f_fic != nullptr) { slice_size = new (nothrow) infinint(f_fic->get_size()); if(slice_size == nullptr) throw Ememory("header::read"); } break; default: if(!lax) throw Erange("header::read", gettext("Badly formatted SAR header (unknown TLV type in slice header)")); else { ui.message(gettext("LAX MODE: Unknown data in slice header, ignoring and continuing")); slice_size = new (nothrow) infinint(0); if(slice_size == nullptr) throw Ememory("header::read"); } } if(data_name.is_cleared()) data_name = internal_name; } void header::write(user_interaction & ui, generic_file & f) const { magic_number tmp; char tmp_ext[] = { extension_tlv, '\0' }; tmp = htonl(magic); f.write((char *)&tmp, sizeof(magic)); internal_name.dump(f); f.write(&flag, 1); if(old_header) { if(first_size != nullptr && slice_size != nullptr && *first_size != *slice_size) { tmp_ext[0] = extension_size; f.write(tmp_ext, 1); slice_size->dump(f); } else { tmp_ext[0] = extension_none; f.write(tmp_ext, 1); } } else { f.write(tmp_ext, 1); // since release 2.4.0, tlv is always used to store optional information build_tlv_list(ui).dump(f); } } bool header::get_first_slice_size(infinint & size) const { if(first_size != nullptr) { size = *first_size; return true; } else return false; } void header::set_first_slice_size(const infinint & size) { if(first_size == nullptr) { first_size = new (nothrow) infinint(); if(first_size == nullptr) throw Ememory("header::set_first_file_size"); } *first_size = size; } bool header::get_slice_size(infinint & size) const { if(slice_size != nullptr) { size = *slice_size; return true; } else return false; } void header::set_slice_size(const infinint & size) { if(slice_size == nullptr) { slice_size = new (nothrow) infinint(); if(slice_size == nullptr) throw Ememory("header::set_slice_size"); } *slice_size = size; } void header::copy_from(const header & ref) { magic = ref.magic; internal_name = ref.internal_name; data_name = ref.data_name; flag = ref.flag; first_size = nullptr; slice_size = nullptr; try { if(ref.first_size != nullptr) { first_size = new (nothrow) infinint(); if(first_size == nullptr) throw Ememory("header::copy_from"); *first_size = *ref.first_size; } if(ref.slice_size != nullptr) { slice_size = new (nothrow) infinint(); if(slice_size == nullptr) throw Ememory("header::copy_from"); *slice_size = *ref.slice_size; } old_header = ref.old_header; } catch(...) { free_pointers(); throw; } } void header::move_from(header && ref) noexcept { magic = move(ref.magic); internal_name = move(ref.internal_name); data_name = move(ref.data_name); flag = move(ref.flag); swap(first_size, ref.first_size); swap(slice_size, ref.slice_size); old_header = move(ref.old_header); } void header::free_pointers() { if(first_size != nullptr) { delete first_size; first_size = nullptr; } if(slice_size != nullptr) { delete slice_size; slice_size = nullptr; } } void header::fill_from(user_interaction & ui, const tlv_list & extension) { U_I taille = extension.size(); free_pointers(); for(U_I index = 0; index < taille; ++index) { switch(extension[index].get_type()) { case tlv_first_size: first_size = new (nothrow) infinint(); if(first_size == nullptr) throw Ememory("header::fill_from"); extension[index].skip(0); first_size->read(extension[index]); break; case tlv_size: slice_size = new (nothrow) infinint(); if(slice_size == nullptr) throw Ememory("header::fill_from"); extension[index].skip(0); slice_size->read(extension[index]); break; case tlv_data_name: try { extension[index].skip(0); data_name.read(extension[index]); } catch(Erange & e) { throw Erange("header::fill_from", gettext("incomplete data set name found in a slice header")); } break; default: ui.pause(tools_printf(gettext("Unknown entry found in slice header (type = %d), option not supported. The archive you are reading may have been generated by a more recent version of libdar, ignore this entry and continue anyway?"), extension[index].get_type())); } } } tlv_list header::build_tlv_list(user_interaction & ui) const { tlv_list ret; tlv tmp; if(first_size != nullptr) { tmp.reset(); first_size->dump(tmp); tmp.set_type(tlv_first_size); ret.add(tmp); } if(slice_size != nullptr) { tmp.reset(); slice_size->dump(tmp); tmp.set_type(tlv_size); ret.add(tmp); } tmp.reset(); data_name.dump(tmp); tmp.set_type(tlv_data_name); ret.add(tmp); return ret; } } // end of namespace dar-2.7.15/src/libdar/archive.hpp0000644000175000017500000004112014636066467013473 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file archive.hpp /// \brief the archive class is defined in this module /// \ingroup API #ifndef ARCHIVE_HPP #define ARCHIVE_HPP #include "../my_config.h" #include #include #include #include "erreurs.hpp" #include "path.hpp" #include "statistics.hpp" #include "archive_options.hpp" #include "list_entry.hpp" #include "crypto.hpp" #include "archive_summary.hpp" #include "archive_listing_callback.hpp" #include "user_interaction.hpp" namespace libdar { // need to declare class database friend of class archive. // class database need to access the archive table of content // though exposing it by mean of a public method of class // archive would let it visibile to the API, thing we do not // want class database; /// \addtogroup API /// @{ /// the archive class realizes the most general operations on archives /// the operations corresponds to the one the final user expects, these /// are the same abstraction level as the operation realized by the DAR /// command line tool. class archive { public: /// this constructor opens an already existing archive (for reading) [this is the "read" constructor] /// \param[in,out] dialog for user- interaction /// \param[in] chem the path where to look for slices /// \param[in] basename the slices basename of the archive to read /// ("-" means standard input, and activates the output_pipe and input_pipe arguments) /// \param[in] extension the slice extension (should always be "dar") /// \param[in] options A set of option to use to read the archive archive(const std::shared_ptr & dialog, const path & chem, const std::string & basename, const std::string & extension, const archive_options_read & options); /// this constuctor create an archive (full or differential) [this is the "create" constructor] /// \param[in,out] dialog for user interaction /// \param[in] fs_root the filesystem to take as root for the backup /// \param[in] sauv_path the path where to create slices /// \param[in] filename base name of the slices. If "-" is given the archive will be produced in standard output /// \param[in] extension slices extension ("dar") /// \param[in] options optional parameters to use for the operation /// \param[out] progressive_report statistics about the operation, considering the treated files (nullptr can be given if you don't want to use this feature) /// \note the statistics fields used are: /// - .treated: the total number of files seen /// - .hard_link: the number of hard linked inodes /// - .tooold: the number of files that changed at the time they were saved and that could not be resaved (due to repeat limit or byte limit) /// - .skipped: number of files not changed (differential backup) /// - .errored: number of files concerned by filesystem error /// - .ignored: number of files excluded by filters /// - .deleted: number of files recorded as deleted /// - .ea_treated: number of entry having some EA /// - .byte_amount : number of wasted bytes due to repeat on change feature /// . archive(const std::shared_ptr & dialog, const path & fs_root, const path & sauv_path, const std::string & filename, const std::string & extension, const archive_options_create & options, statistics * progressive_report); /// this constructor builds an archive from two given archive [this is the "merge" constructor] /// \param[in,out] dialog for user interaction /// \param[in] sauv_path the path where to create slices /// \param[in] ref_arch1 the first mandatory input archive (the second is optional and provided within the 'option' argument /// \param[in] filename base name of the slices. If "-" is given the archive will be produced in standard output /// \param[in] extension slices extension ("dar") /// \param[in] options optional parameters to be used for the operation /// \param[out] progressive_report statistics about the operation, considering the treated files (nullptr can be given if you don't want to use this feature) /// \note the statistics fields used are: /// - .treated: the total number of files seen /// - .hard_link: the number of hard linked inodes /// - .ignored: number of files excluded by filters /// - .deleted: number of files recorded as deleted /// - .ea_treated: number of entry with EA /// . archive(const std::shared_ptr & dialog, const path & sauv_path, std::shared_ptr ref_arch1, const std::string & filename, const std::string & extension, const archive_options_merge & options, statistics * progressive_report); /// this constructor create a new archive from a damaged one [this is the "repair" constructor] /// \param[in,out] dialog for user interaction /// \param[in] chem_src the path where to look for slices of the archive to repair /// \param[in] basename_src the slices basename of the archive to repair /// \param[in] extension_src the slices extension of the archive to repair /// \param[in] options_read the set of option to use to read the archive repair /// \param[in] chem_dst the path where to write the repaired archive /// \param[in] basename_dst the slices basename of the repaired archive /// \param[in] extension_dst the slices extension of the repaired archive /// \param[in] options_repair the set of option to use to write the repaired archive archive(const std::shared_ptr & dialog, const path & chem_src, const std::string & basename_src, const std::string & extension_src, const archive_options_read & options_read, const path & chem_dst, const std::string & basename_dst, const std::string & extension_dst, const archive_options_repair & options_repair); /// copy constructor (not implemented, throw an exception if called explicitely or implicitely) /// \note this lack of implementation is intentionnal, Archive should rather be manipulated /// using pointers, or passed as constant reference (const &) in arguments or returned values. /// Moreover, having two objets one copy of the other may lead to unexpected behaviors while /// merging or creating, isolating or merging archives. archive(const archive & ref) = delete; archive(archive && ref) = delete; archive & operator = (const archive & ref) = delete; archive & operator = (archive && ref) = delete; /// the destructor ~archive(); /// extraction of data from an archive /// \param[in] fs_root the filesystem to take as root for the restoration /// \param[in] options optional parameter to be used for the operation /// \param[in,out] progressive_report points to an already existing statistics object that can be consulted at any time /// during the call (see the returned value to know the useful fields and their meining), /// nullptr can be given in argument if you only need the result at the end of the operation through the returned value of this call /// this should speed up the operation by a little amount. /// \return the statistics about the operation, considering the treated files /// \note the statistics fields used are: /// - .treated: the total number of files restored /// - .skipped: number of files not saved in the archive /// - .tooold: number of file not restored due to overwriting policy decision /// - .errored: number of files concerned by filesystem error /// - .ignored: number of files excluded by filters /// - .deleted: number of files deleted /// - .hard_links: number of hard link restored /// - .ea_treated: number of entry having some EA /// . statistics op_extract(const path &fs_root, const archive_options_extract & options, statistics *progressive_report); /// display a summary of the archive /// \note see also get_stats() method void summary(); /// same information as summary() but as broken out data archive_summary summary_data(); /// listing of the archive contents /// \param[in] callback callback function used to provide data in splitted field (not used if null is given) /// \param[in] context will be passed as is to the last argument of the provided callback /// \param[in] options list of optional parameters to use for the operation /// \note if callback is nullptr (or NULL), the output is done using user_interaction provided with archive constructor /// \note alternative way to get archive contents: /// . archive::get_children_of() method /// . archive::init_catalogue()+get_children_in_table() void op_listing(archive_listing_callback callback, void *context, const archive_options_listing & options) const; /// archive comparison with filesystem /// \param[in] fs_root the filesystem to take as root for the comparison /// \param[in] options optional parameters to be used with the operation /// \param[in,out] progressive_report points to an already existing statistics object that can be consulted at any time /// during the call (see the returned value to know the useful fields and their meining), /// nullptr can be given in argument if you only need the result at the end of the operation through the returned value of this call /// this should speed up the operation by a little amount. /// \return the statistics about the operation, considering the treated files /// \note the statistics fields used are: /// - .treated: the total number of files seen /// - .errored: number of files that do not match or could not be read /// - .ignored: number of files excluded by filters /// . statistics op_diff(const path & fs_root, const archive_options_diff & options, statistics * progressive_report); /// test the archive integrity /// \param[in] options optional parameter to use for the operation /// \param[in,out] progressive_report points to an already existing statistics object that can be consulted at any time /// during the call (see the returned value to know the useful fields and their meining), /// nullptr can be given in argument if you only need the result at the end of the operation through the returned value of this call /// this should speed up the operation by a little amount. /// \note op_test will generate an error message if used on an archive /// that has been created by the isolate or creation constructor /// this is not only an implementation limitation but also a choice. /// testing an file archive using the C++ object used to create /// the file is not a good idea. You need to first destroy this /// C++ object then create a new one with the reading constructor /// this way only you can be sure your archive is properly tested. /// \return the statistics about the operation, considering the treated files /// \note the statistics fields used are: /// - .treated: the total number of files seen /// - .skipped: number of file older than the one on filesystem /// - .errored: number of files with error /// . statistics op_test(const archive_options_test & options, statistics * progressive_report); /// this methodes isolates the catalogue of a the current archive into a separated archive /// \param[in] sauv_path the path where to create slices /// \param[in] filename base name of the slices ("-" for standard output) /// \param[in] extension slices extension ("dar") /// \param[in] options optional parameters to use for the operation /// \note that if the archive contains delta sig and isolation options, specifying not /// to keep them in the resulting isolated catalogue leads the current archive object /// (but not the corresponding archive stored on filesystem) to be modified /// (delta signature are removed) --- this is not a const method. void op_isolate(const path &sauv_path, const std::string & filename, const std::string & extension, const archive_options_isolate & options); /// getting information about a given directory /// \param[in] callback callback function used to provide data in splitted field /// \param[in] context will be passed as first argument of the callback as is provided here /// \param[in] dir relative path the directory to get information about /// \param[in] fetch_ea whether to look for EA (not possible in sequential read mode) /// \return true if some children have been found and the callback has been run at least once bool get_children_of(archive_listing_callback callback, void *context, const std::string & dir, bool fetch_ea = false); /// getting information about the given directory (alternative to get_children_of) /// \param[in] dir relative path the directory to get information about, use empty string for root directory /// \param[in] fetch_ea whether to fetch Extended Attributes relative information in each returned list_entry (not possible in sequential read mode) /// \return a table information about all subdir and subfile for the given directory /// \note at the difference of get_children_of, this call does not rely on a user_interaction class /// to provide the information, but rather returns a table of children. To allow new fields to /// be added to the future the table contains an object that provide a method per field. /// \note before calling this method on this object, a single call to init_catalogue() is /// mandatory const std::vector get_children_in_table(const std::string & dir, bool fetch_ea = false) const; /// returns true if the pointed directory has one or more subdirectories bool has_subdirectory(const std::string & dir) const; /// retrieving statistics about archive contents const entree_stats get_stats() const; /// retrieving signature information about the archive const std::list & get_signatories() const; /// necessary to get the catalogue fully loaded in memory in any situation /// in particular in sequential reading mode void init_catalogue() const; /// closes all filedescriptors and associated even when in sequential read mode /// \note once this method has been called, the archive object can only be used /// as reference for a differential archive. void drop_all_filedescriptors(); /// change all inode as unsaved (equal to differential backup with no change met) void set_to_unsaved_data_and_FSA(); /// get the first slice header /// get_first_slice_header_size() and get_non_first_slice_header_size() can be used to translate /// from archive offset as reported by the list_entry::get_archive_offset_*() methods to file /// offset. This can be done by adding the first_slice_header_size to the archive offset, if the /// resulting number is larger than the first slice size, substract the it and add the non_first_slice /// header_size, and so on. This way you can determin the slice number to look into and the file offset /// in that file. Unit for all value is the byte (= octet). /// \note may return 0 if the slice header is not known U_64 get_first_slice_header_size() const; /// get the non first slice header /// \note may return 0 if the slice header is not known U_64 get_non_first_slice_header_size() const; private: class i_archive; std::shared_ptr pimpl; // see the comment near the forward declaration // of class database above for explanations friend class database; }; } // end of namespace #endif dar-2.7.15/src/libdar/i_entrepot_libcurl.cpp0000644000175000017500000003133214636067146015730 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "tools.hpp" #include "i_entrepot_libcurl.hpp" #include "fichier_libcurl.hpp" #include "cache_global.hpp" using namespace std; namespace libdar { #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) entrepot_libcurl::i_entrepot_libcurl::i_entrepot_libcurl(const shared_ptr & dialog, //< for user interaction mycurl_protocol proto, //< network protocol to use const string & login, //< user login on remote host const secu_string & password, //< user password on remote host (empty for file auth or user interaction) const string & host, //< the remote server to connect to const string & port, //< TCP/UDP port to connec to (empty string for default) bool auth_from_file, //< whether to check $HOME/.netrc for password const string & sftp_pub_keyfile, //< where to fetch the public key (sftp only) const string & sftp_prv_keyfile, //< where to fetch the private key (sftp only) const string & sftp_known_hosts, //< location of the known_hosts file (empty string to disable this security check) U_I waiting_time, bool verbose): mem_ui(dialog), x_proto(proto), base_URL(build_url_from(proto, host, port)), wait_delay(waiting_time) { verbosity = verbose; current_dir.clear(); reading_dir_tmp.clear(); // initializing the fields from parent class entrepot set_root(path("/")); set_location(path("/")); set_user_ownership(""); // not used for this type of entrepot set_group_ownership(""); // not used for this type of entrepot if(!mycurl_is_protocol_available(proto)) { string named_proto = mycurl_protocol2string(proto); throw Erange("entrepot_libcurl::i_entrepot_libcurl::i_entrepot_libcurl", tools_printf(gettext("protocol %S is not supported by libcurl, aborting"), & named_proto)); } set_libcurl_authentication(*dialog, host, login, password, auth_from_file, sftp_pub_keyfile, sftp_prv_keyfile, sftp_known_hosts); if(verbose) { easyh.setopt_global(CURLOPT_VERBOSE,(long)1); get_ui().printf(gettext("repository parameters passed to libcurl:")); get_ui().printf(gettext(" hostname\t: %s\n port \t: %s\n login \t: %s\n password\t: (hidden)"), host.c_str(), port.c_str(), login.c_str()); get_ui().printf(gettext(" base URL\t: %s"), base_URL.c_str()); } } void entrepot_libcurl::i_entrepot_libcurl::read_dir_reset() const { long listonly; i_entrepot_libcurl *me = const_cast(this); shared_ptr node; if(me == nullptr) throw SRC_BUG; node = me->easyh.alloc_instance(); if(!node) throw SRC_BUG; if(verbosity) get_ui().printf("Asking libcurl to read directory content at %s", get_libcurl_URL().c_str()); me->current_dir.clear(); me->reading_dir_tmp = ""; switch(x_proto) { case proto_ftp: case proto_sftp: listonly = 1; node->setopt(CURLOPT_URL, get_libcurl_URL()); node->setopt(CURLOPT_DIRLISTONLY, listonly); node->setopt(CURLOPT_WRITEFUNCTION, (void*)get_ftp_listing_callback); node->setopt(CURLOPT_WRITEDATA, (void*)(this)); node->apply(get_pointer(), wait_delay); // libcurl will invoke our callback function 'get_ftp_listing_callback' // passed above as argument, which one will fill the directory content // in the std::deque current_dir. The reading_dir_tmp is the current // or the latest entry name of the directory that was under the process of // being spelled... if(!reading_dir_tmp.empty()) { me->current_dir.push_back(reading_dir_tmp); me->reading_dir_tmp.clear(); } break; default: throw SRC_BUG; } } bool entrepot_libcurl::i_entrepot_libcurl::read_dir_next(string & filename) const { i_entrepot_libcurl *me = const_cast(this); if(me == nullptr) throw SRC_BUG; if(current_dir.empty()) return false; else { filename = current_dir.front(); me->current_dir.pop_front(); return true; } } fichier_global *entrepot_libcurl::i_entrepot_libcurl::inherited_open(const shared_ptr & dialog, const string & filename, gf_mode mode, bool force_permission, U_I permission, bool fail_if_exists, bool erase) const { fichier_global *ret = nullptr; cache_global *rw = nullptr; i_entrepot_libcurl *me = const_cast(this); gf_mode hidden_mode = mode; if(me == nullptr) throw SRC_BUG; if(fail_if_exists) { string tmp; me->read_dir_reset(); while(me->read_dir_next(tmp)) if(tmp == filename) throw Esystem("i_entrepot_libcurl::inherited_open", "File exists on remote repository" , Esystem::io_exist); } string chemin = (path(get_url(), true).append(filename)).display(); if(verbosity) get_ui().printf("Asking libcurl to open the file %s", chemin.c_str()); if(hidden_mode == gf_read_write) hidden_mode = gf_write_only; try { fichier_libcurl *ret_libcurl = new (nothrow) fichier_libcurl(dialog, chemin, x_proto, me->easyh.alloc_instance(), hidden_mode, wait_delay, force_permission, permission, erase); ret = ret_libcurl; if(ret == nullptr) throw Ememory("entrepot_libcurl::i_entrepot_libcurl::inherited_open"); switch(mode) { case gf_read_write: rw = new (nothrow) cache_global(dialog, ret, true); if(rw != nullptr) { ret = nullptr; // the former object pointed to by ret is now managed by rw rw->change_to_read_write(); ret = rw; // only now because if change_to_read_write() generate an exception // we must not delete twice the same object that would be pointed to once by ret and once again by rw rw = nullptr; } else throw Ememory("entrpot_libcurl::inherited_open"); break; case gf_read_only: rw = new (nothrow) cache_global(dialog, ret, false); if(rw != nullptr) { ret = rw; // the former object pointed to by ret is now managed by rw rw = nullptr; } break; case gf_write_only: // small cache to avoid trailer and header writes byte per byte // (1000 bytes ~ payload of an classical ethernet non-jumbo frame) rw = new (nothrow) cache_global(dialog, ret, false, 1000); if(rw != nullptr) { ret = rw; rw = nullptr; } break; default: throw SRC_BUG; } } catch(...) { if(ret != nullptr) delete ret; if(rw != nullptr) delete rw; throw; } return ret; } void entrepot_libcurl::i_entrepot_libcurl::inherited_unlink(const string & filename) const { mycurl_slist headers; shared_ptr node; string order; i_entrepot_libcurl *me = const_cast(this); if(me == nullptr) throw SRC_BUG; node = me->easyh.alloc_instance(); if(!node) throw SRC_BUG; if(verbosity) get_ui().printf("Asking libcurl to delete file %s", filename.c_str()); switch(x_proto) { case proto_ftp: case proto_sftp: if(x_proto == proto_ftp) order = "DELE " + filename; else order = "rm " + filename; headers.append(order); node->setopt(CURLOPT_QUOTE, headers); node->setopt(CURLOPT_URL, get_libcurl_URL()); node->setopt(CURLOPT_NOBODY, (long)1); node->apply(get_pointer(), wait_delay); break; default: throw SRC_BUG; } } void entrepot_libcurl::i_entrepot_libcurl::read_dir_flush() { current_dir.clear(); } string entrepot_libcurl::i_entrepot_libcurl::get_libcurl_URL() const { string target = get_url(); if(target.size() == 0) throw SRC_BUG; else if(target[target.size() - 1] != '/') target += "/"; return target; } void entrepot_libcurl::i_entrepot_libcurl::set_libcurl_authentication(user_interaction & dialog, const string & location, const string & login, const secu_string & password, bool auth_from_file, const string & sftp_pub_keyfile, const string & sftp_prv_keyfile, const string & sftp_known_hosts) { secu_string real_pass = password; string real_login = login; // checking server authentication switch(x_proto) { case proto_ftp: break; case proto_sftp: if(!sftp_known_hosts.empty()) easyh.setopt_global(CURLOPT_SSH_KNOWNHOSTS, sftp_known_hosts); else dialog.message("Warning: known_hosts file check has been disabled, connecting to remote host is subjet to man-in-the-middle attack and login/password credential for remote sftp server to be stolen"); easyh.setopt_global(CURLOPT_SSH_PUBLIC_KEYFILE, sftp_pub_keyfile); easyh.setopt_global(CURLOPT_SSH_PRIVATE_KEYFILE, sftp_prv_keyfile); easyh.setopt_global(CURLOPT_SSH_AUTH_TYPES, (long)(CURLSSH_AUTH_PUBLICKEY|CURLSSH_AUTH_PASSWORD)); break; default: throw SRC_BUG; } // checking for user credentials if(real_login.empty()) real_login = "anonymous"; if(auth_from_file) { easyh.setopt_global(CURLOPT_USERNAME, real_login); easyh.setopt_global(CURLOPT_NETRC, (long)(CURL_NETRC_OPTIONAL)); } else // login + password authentication { if(password.empty()) { real_pass = get_ui().get_secu_string(tools_printf(gettext("Please provide the password for login %S at host %S: "), &real_login, &location), false); } secu_string auth(real_login.size() + 1 + real_pass.get_size() + 1); auth.append(real_login.c_str(), real_login.size()); auth.append(":", 1); auth.append(real_pass.c_str(), real_pass.get_size()); easyh.setopt_global(CURLOPT_USERPWD, auth); } } string entrepot_libcurl::i_entrepot_libcurl::mycurl_protocol2string(mycurl_protocol proto) { string ret; switch(proto) { case proto_ftp: ret = "ftp"; break; case proto_sftp: ret = "sftp"; break; default: throw SRC_BUG; } return ret; } string entrepot_libcurl::i_entrepot_libcurl::build_url_from(mycurl_protocol proto, const string & host, const string & port) { string ret = mycurl_protocol2string(proto) + "://" + host; if(!port.empty()) ret += ":" + port; ret += "/"; // to have any path added in the future to refer to the root // of the remote repository and not relative to the landing directory return ret; } size_t entrepot_libcurl::i_entrepot_libcurl::get_ftp_listing_callback(void *buffer, size_t size, size_t nmemb, void *userp) { i_entrepot_libcurl *me = (i_entrepot_libcurl *)(userp); char *ptr = (char *)buffer; if(me == nullptr) return size > 0 ? 0 : 1; // report error to libcurl for(unsigned int mi = 0; mi < nmemb; ++mi) for(unsigned int i = 0; i < size; ++i) { switch(*ptr) { case '\n': me->current_dir.push_back(me->reading_dir_tmp); me->reading_dir_tmp.clear(); break; case '\r': break; // just ignore it default: // normal character me->reading_dir_tmp += *ptr; break; } ++ptr; } return size*nmemb; } bool entrepot_libcurl::i_entrepot_libcurl::mycurl_is_protocol_available(mycurl_protocol proto) { curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); const char **ptr = nullptr; string named_proto = mycurl_protocol2string(proto); if(data == nullptr) throw SRC_BUG; ptr = const_cast(data->protocols); if(ptr == nullptr) throw SRC_BUG; while(*ptr != nullptr && strcmp(*ptr, named_proto.c_str()) != 0) ++ptr; return *ptr != nullptr; } #endif } // end of namespace dar-2.7.15/src/libdar/tools.cpp0000644000175000017500000020276514636067146013216 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_WAIT_H # include #endif #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #ifndef WIFSTOPPED #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f) #endif #ifndef WIFSIGNALED # define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) #endif #ifndef WTERMSIG #define WTERMSIG(status) ((status) & 0x7f) #endif #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SIGNAL_H #include #endif #if HAVE_SYS_TYPE_H #include #endif #if HAVE_UTIME_H #include #endif #if HAVE_SYS_TIME_H #include #endif #if HAVE_CTYPE_H #include #endif #if HAVE_PWD_H #include #endif #if HAVE_GRP_H #include #endif #if HAVE_STDLIB_H #include #endif #if HAVE_SYS_UTSNAME_H #include #endif #if HAVE_WCHAR_H #include #endif #if HAVE_WCTYPE_H #include #endif #if HAVE_STDDEF_H #include #endif #if HAVE_DIRENT_H #include #endif } // end extern "C" #include #include #include #include "nls_swap.hpp" #include "tools.hpp" #include "erreurs.hpp" #include "deci.hpp" #include "user_interaction.hpp" #include "integers.hpp" #include "mask.hpp" #include "etage.hpp" #ifdef __DYNAMIC__ #include "user_group_bases.hpp" #endif #include "compile_time_features.hpp" #include "memory_file.hpp" #include "tuyau.hpp" using namespace std; namespace libdar { #ifdef __DYNAMIC__ // Yes, this is a static variable, // it contains the necessary mutex to keep libdar thread-safe static const user_group_bases *user_group = nullptr; #endif // the following variable is static this breaks the threadsafe support // while it also concerns the signaling which is out process related static void runson(user_interaction & dialog, char * const argv[]); static void ignore_deadson(S_I sig); static void abort_on_deadson(S_I sig); void tools_init() { #ifdef __DYNAMIC__ if(user_group == nullptr) { user_group = new (nothrow) user_group_bases(); if(user_group == nullptr) throw Ememory("tools_init"); } #endif } void tools_end() { #ifdef __DYNAMIC__ if(user_group != nullptr) { delete user_group; user_group = nullptr; } #endif } void tools_write_string(generic_file & f, const string & s) { tools_write_string_all(f, s); f.write("", 1); // adding a '\0' at the end; } void tools_read_string(generic_file & f, string & s) { char a[2] = { 0, 0 }; S_I lu; s = ""; do { lu = f.read(a, 1); if(lu == 1 && a[0] != '\0') s += a; } while(lu == 1 && a[0] != '\0'); if(lu != 1 || a[0] != '\0') throw Erange("tools_read_string", dar_gettext("Not a zero terminated string in file")); } void tools_write_string_all(generic_file & f, const string & s) { f.write(s.c_str(), s.size()); } void tools_read_string_size(generic_file & f, string & s, infinint taille) { U_16 small_read = 0; U_I max_read = 0; S_I lu = 0; const U_I buf_size = 10240; char buffer[buf_size]; s = ""; do { if(small_read > 0) { max_read = small_read > buf_size ? buf_size : small_read; lu = f.read(buffer, max_read); small_read -= lu; s += string((char *)buffer, (char *)buffer+lu); } taille.unstack(small_read); } while(small_read > 0); } infinint tools_get_filesize(const path &p) { struct stat buf; if(lstat(p.display().c_str(), &buf) < 0) { string tmp = tools_strerror_r(errno); throw Erange("tools_get_filesize", tools_printf(dar_gettext("Cannot get file size: %s"), tmp.c_str())); } return (U_32)buf.st_size; } string tools_display_integer_in_metric_system(infinint number, const string & unit, bool binary) { string ret = ""; infinint multiple = binary ? 1024 : 1000; U_I power = 0; // 1 = 'k', 2 = 'M', 3 = 'G', 4 = 'T', 5 = 'P', 6 = 'E', 7 = 'Z', 8 = 'Y' while(number >= multiple && power < 8) { ++power; number /= multiple; } ret = deci(number).human(); switch(power) { case 0: if(!number.is_zero()) ret += " " + unit; // not displaying unit for zero for clarity in particular when octets symbol is used // which would give "0 o" that is somehow not very easy to read/understand break; case 1: ret += string(" ") + (binary ? "ki" : "k") + unit; break; case 2: ret += string(" ") + (binary ? "Mi" : "M") + unit; break; case 3: ret += string(" ") + (binary ? "Gi" : "G") + unit; break; case 4: ret += string(" ") + (binary ? "Ti" : "T") + unit; break; case 5: ret += string(" ") + (binary ? "Pi" : "P") + unit; break; case 6: ret += string(" ") + (binary ? "Ei" : "E") + unit; break; case 7: ret += string(" ") + (binary ? "Zi" : "Z") + unit; break; default: ret += string(" ") + (binary ? "Yi" : "Y") + unit; break; } return ret; } string::iterator tools_find_last_char_of(string &s, unsigned char v) { if(s.empty()) return s.end(); string::iterator back, it = s.begin(); bool valid = (it != s.end()) && (*it == v); while(it != s.end()) { back = it; it = find(it + 1, s.end(), v); } if(!valid && (back == s.begin())) // no char found at all (back has been sticked at the beginning and the first character is not the one we look for return s.end(); else return back; } void tools_blocking_read(S_I fd, bool mode) { S_I flags = fcntl(fd, F_GETFL, 0); if(flags < 0) throw Erange("tools_blocking_read", string(dar_gettext("Cannot read \"fcntl\" file's flags : "))+tools_strerror_r(errno)); if(!mode) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if(fcntl(fd, F_SETFL, flags) < 0) throw Erange("tools_blocking_read", string(dar_gettext("Cannot set \"fcntl\" file's flags : "))+tools_strerror_r(errno)); } string tools_name_of_uid(const infinint & uid) { #ifndef __DYNAMIC__ string name = ""; #else string name; if(user_group != nullptr) name = user_group->get_username(uid); else throw SRC_BUG; #endif if(name.empty()) // uid not associated with a name { deci d = uid; return d.human(); } else return name; } string tools_name_of_gid(const infinint & gid) { #ifndef __DYNAMIC__ string name = ""; #else string name; if(user_group != nullptr) name = user_group->get_groupname(gid); else throw SRC_BUG; #endif if(name.empty()) // uid not associated with a name { deci d = gid; return d.human(); } else return name; } string tools_uword2str(U_16 x) { ostringstream tmp; tmp << x; return tmp.str(); } string tools_int2str(S_I x) { ostringstream tmp; tmp << x; return tmp.str(); } string tools_uint2str(U_I x) { ostringstream tmp; tmp << x; return tmp.str(); } string tools_addspacebefore(string s, U_I expected_size) { return string(expected_size - s.size(), ' ') + s; } string tools_display_date(const datetime & date) { time_t pas = 0; time_t frac = 0; string ret; if(!date.get_value(pas, frac, datetime::tu_second)) // conversion to system type failed. Using a replacement string return deci(date.get_second_value()).human(); else { char *val = nullptr; #if HAVE_CTIME_R char *str = new (nothrow) char [50]; //< minimum required is 26 bytes if(str == nullptr) throw Ememory("tools_display_date"); try { val = ctime_r(&pas, str); #else val = ctime(&pas); #endif if(val == nullptr) // ctime() failed ret = tools_int2str(pas); else ret = val; #if HAVE_CTIME_R } catch(...) { delete [] str; throw; } delete [] str; #else ret = val; #endif } return string(ret.begin(), ret.end() - 1); // -1 to remove the ending '\n' } char *tools_str2charptr(const string &x) { U_I size = x.size(); char *ret = new (nothrow) char[size+1]; if(ret == nullptr) throw Ememory("line_tools_str2charptr"); (void)memcpy(ret, x.c_str(), size); ret[size] = '\0'; return ret; } U_I tools_str2int(const string & x) { stringstream tmp(x); U_I ret; string residu; if((tmp >> ret).fail()) throw Erange("line_tools_str2string", string(dar_gettext("Invalid number: ")) + x); tmp >> residu; for(U_I i = 0; i < residu.size(); ++i) if(residu[i] != ' ') throw Erange("line_tools_str2string", string(dar_gettext("Invalid number: ")) + x); return ret; } void tools_system(user_interaction & dialog, const vector & argvector) { if(argvector.empty()) return; // nothing to do // ISO C++ forbids variable-size array char **argv = new (nothrow) char * [argvector.size()+1]; for(U_I i = 0; i <= argvector.size(); i++) argv[i] = nullptr; try { S_I status; bool loop; for(U_I i = 0; i < argvector.size(); i++) argv[i] = tools_str2charptr(argvector[i]); argv[argvector.size()] = nullptr; // this is already done above but that does not hurt doing it twice :-) do { ignore_deadson(0); loop = false; S_I pid = fork(); switch(pid) { case -1: throw Erange("tools_system", string(dar_gettext("Error while calling fork() to launch dar: ")) + tools_strerror_r(errno)); case 0: // fork has succeeded, we are the child process try { runson(dialog, argv); // function that never returns or throws exceptions throw SRC_BUG; // just in case the previous function returned } catch(...) { throw SRC_BUG; } default: if(wait(&status) <= 0) throw Erange("tools_system", string(dar_gettext("Unexpected error while waiting for dar to terminate: ")) + tools_strerror_r(errno)); else // checking the way dar has exit if(WIFSIGNALED(status)) // exited because of a signal { try { dialog.pause(string(dar_gettext("DAR terminated upon signal reception: ")) #if HAVE_DECL_SYS_SIGLIST + (WTERMSIG(status) < NSIG ? sys_siglist[WTERMSIG(status)] : tools_int2str(WTERMSIG(status))) #else + tools_int2str(WTERMSIG(status)) #endif + dar_gettext(" . Retry to launch dar as previously ?")); loop = true; } catch(Euser_abort & e) { dialog.pause(dar_gettext(" Continue anyway ?")); } } else // normal terminaison checking exit status code if(WEXITSTATUS(status) != 0) dialog.pause(string(dar_gettext("DAR sub-process has terminated with exit code ")) + tools_int2str(WEXITSTATUS(status)) + dar_gettext(" Continue anyway ?")); } } while(loop); } catch(...) { for(U_I i = 0; i <= argvector.size(); i++) if(argv[i] != nullptr) delete [] argv[i]; delete [] argv; throw; } for(U_I i = 0; i <= argvector.size(); i++) if(argv[i] != nullptr) delete [] argv[i]; delete [] argv; } void tools_system_with_pipe(const shared_ptr & dialog, const string & dar_cmd, const vector & argvpipe) { const char *argv[] = { dar_cmd.c_str(), "--pipe-fd", nullptr, nullptr }; bool loop = false; if(!dialog) throw SRC_BUG; // dialog points to nothing do { tuyau *tube = nullptr; try { tube = new (nothrow) tuyau(dialog); if(tube == nullptr) throw Ememory("tools_system_with_pipe"); const string read_fd = tools_int2str(tube->get_read_fd()); tlv_list pipeargs; S_I status; argv[2] = read_fd.c_str(); signal(SIGCHLD, &abort_on_deadson); // do not accept child death loop = false; S_I pid = fork(); switch(pid) { case -1: throw Erange("tools_system_with_pipe", string(dar_gettext("Error while calling fork() to launch dar: ")) + tools_strerror_r(errno)); case 0: // fork has succeeded, we are the child process try { if(tube != nullptr) { tube->do_not_close_read_fd(); delete tube; // C++ object is destroyed but read filedescriptor has been kept open tube = nullptr; runson(*dialog, const_cast(argv)); throw SRC_BUG; } else throw SRC_BUG; } catch(...) { throw SRC_BUG; } default: // fork has succeeded, we are the parent process tube->close_read_fd(); pipeargs = tools_string2tlv_list(*dialog, 0, argvpipe); pipeargs.dump(*tube); ignore_deadson(0); // now we can ignore SIGCHLD signals just before destroying the pipe filedescriptor, which will trigger and EOF while reading on pipe // in the child process delete tube; tube = nullptr; if(wait(&status) <= 0) throw Erange("tools_system", string(dar_gettext("Unexpected error while waiting for dar to terminate: ")) + tools_strerror_r(errno)); else // checking the way dar has exit if(WIFSIGNALED(status)) // exited because of a signal { try { dialog->pause(string(dar_gettext("DAR terminated upon signal reception: ")) #if HAVE_DECL_SYS_SIGLIST + (WTERMSIG(status) < NSIG ? sys_siglist[WTERMSIG(status)] : tools_int2str(WTERMSIG(status))) #else + tools_int2str(WTERMSIG(status)) #endif + dar_gettext(" . Retry to launch dar as previously ?")); loop = true; } catch(Euser_abort & e) { dialog->pause(dar_gettext(" Continue anyway ?")); } } else // normal terminaison checking exit status code if(WEXITSTATUS(status) != 0) dialog->pause(string(dar_gettext("DAR sub-process has terminated with exit code ")) + tools_int2str(WEXITSTATUS(status)) + dar_gettext(" Continue anyway ?")); } } catch(...) { if(tube != nullptr) delete tube; throw; } if(tube != nullptr) delete tube; } while(loop); } void tools_write_vector(generic_file & f, const vector & x) { infinint tmp = x.size(); vector::const_iterator it = x.begin(); tmp.dump(f); while(it != x.end()) tools_write_string(f, *it++); } void tools_read_vector(generic_file & f, vector & x) { infinint tmp = infinint(f); string elem; x.clear(); while(!tmp.is_zero()) { tools_read_string(f, elem); x.push_back(elem); tmp--; } } string tools_concat_vector(const string & separator, const vector & x) { string ret = separator; vector::const_iterator it = x.begin(); while(it != x.end()) ret += *it++ + separator; return ret; } vector operator + (vector a, vector b) { vector::iterator it = b.begin(); while(it != b.end()) a.push_back(*it++); return a; } bool tools_is_equal_with_hourshift(const infinint & hourshift, const datetime & date1, const datetime & date2) { infinint num, rest; datetime t_delta = date1 > date2 ? date1.loose_diff(date2) : date2.loose_diff(date1); infinint delta; if(t_delta.is_null()) return true; // both args are equal without any hourshift consideration if(!t_delta.is_integer_second()) return false; // difference is not an integer number of second else delta = t_delta.get_second_value(); // delta = 3600*num + rest // with 0 <= rest < 3600 // (this is euclidian division) euclide(delta, 3600, num, rest); if(!rest.is_zero()) return false; // difference is not a integer number of hour else // rest == 0 return num <= hourshift; } string tools_readlink(const char *root) { U_I length = 10240; char *buffer = nullptr; S_I lu; string ret = ""; if(root == nullptr) throw Erange("tools_readlink", dar_gettext("nullptr argument given to tools_readlink()")); if(strcmp(root, "") == 0) throw Erange("tools_readlink", dar_gettext("Empty string given as argument to tools_readlink()")); try { do { buffer = new (nothrow) char[length]; if(buffer == nullptr) throw Ememory("tools_readlink"); lu = readlink(root, buffer, length-1); // length-1 to have room to add '\0' at the end if(lu < 0) // error occured with readlink { string tmp; switch(errno) { case EINVAL: // not a symbolic link (thus we return the given argument) ret = root; break; case ENAMETOOLONG: // too small buffer delete [] buffer; buffer = nullptr; length *= 2; break; default: // other error tmp = tools_strerror_r(errno); throw Erange("get_readlink", tools_printf(dar_gettext("Cannot read file information for %s : %s"), root, tmp.c_str())); } } else // got the correct real path of symlink if((U_I)(lu) < length) { buffer[lu] = '\0'; ret = buffer; } else // "lu" should not be greater than length: readlink system call error { // trying to workaround with a larger buffer delete [] buffer; buffer = nullptr; length *= 2; } } while(ret == ""); } catch(...) { if(buffer != nullptr) delete [] buffer; throw; } if(buffer != nullptr) delete [] buffer; return ret; } void tools_make_date(const std::string & chemin, bool symlink, const datetime & access, const datetime & modif, const datetime & birth) { int ret; time_t tmp = 0; time_t frac = 0; datetime::time_unit tunit; #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND struct timespec nano[2]; tunit = datetime::tu_nanosecond; #else #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND struct timeval micro[2]; tunit = datetime::tu_microsecond; #else struct utimbuf secs; tunit = datetime::tu_second; #endif #endif if(!access.get_value(tmp, frac, tunit)) throw Erange("tools_make_date", "cannot set atime of file, value too high for the system integer type"); // the first time, setting modification time to the value of birth time // systems that supports birth time update birth time if the given mtime is older than the current birth time // so here we assume birth < modif (if not the birth time will be set to modif) // we run a second time the same call but with the real mtime, which should not change the birthtime if this // one is as expected older than mtime. else { #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND nano[0].tv_sec = tmp; nano[0].tv_nsec = frac; #else #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND micro[0].tv_sec = tmp; micro[0].tv_usec = frac; #else secs.actime = tmp; #endif #endif } if(birth != modif) { if(!birth.get_value(tmp, frac, tunit)) throw Erange("tools_make_date", "cannot set birth time of file, value too high for the system integer type"); else { #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND nano[1].tv_sec = tmp; nano[1].tv_nsec = frac; #else #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND micro[1].tv_sec = tmp; micro[1].tv_usec = frac; #else secs.modtime = tmp; #endif #endif } #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND ret = utimensat(0, chemin.c_str(), nano, AT_SYMLINK_NOFOLLOW); #else #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND #ifdef HAVE_LUTIMES ret = lutimes(chemin.c_str(), micro); #else if(symlink) return; // not able to restore dates of symlinks ret = utimes(chemin.c_str(), micro); #endif #else if(symlink) return; // not able to restore dates of symlinks ret = utime(chemin.c_str() , &secs); #endif #endif if(ret < 0) Erange("tools_make_date", string(dar_gettext("Cannot set birth time: ")) + tools_strerror_r(errno)); } // we set atime and mtime here if(!modif.get_value(tmp, frac, tunit)) throw Erange("tools_make_date", "cannot set last modification time of file, value too high for the system integer type"); else { #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND nano[1].tv_sec = tmp; nano[1].tv_nsec = frac; #else #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND micro[1].tv_sec = tmp; micro[1].tv_usec = frac; #else secs.modtime = tmp; #endif #endif } #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND ret = utimensat(0, chemin.c_str(), nano, AT_SYMLINK_NOFOLLOW); #else #if LIBDAR_TIME_WRITE_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND #ifdef HAVE_LUTIMES ret = lutimes(chemin.c_str(), micro); #else if(symlink) return; // not able to restore dates of symlinks ret = utimes(chemin.c_str(),m icro); #endif #else if(symlink) return; // not able to restore dates of symlinks ret = utime(chemin.c_str() , &secs); #endif #endif if(ret < 0) throw Erange("tools_make_date", string(dar_gettext("Cannot set last access and last modification time: ")) + tools_strerror_r(errno)); } void tools_noexcept_make_date(const string & chem, bool symlink, const datetime & last_acc, const datetime & last_mod, const datetime & birth) { try { if(!last_acc.is_null() || !last_mod.is_null()) tools_make_date(chem, symlink, last_acc, last_mod, birth); // else the directory could not be openned properly // and time could not be retrieved, so we don't try // to restore them } catch(Erange & e) { // cannot restore dates, ignoring } } bool tools_is_case_insensitive_equal(const string & a, const string & b) { U_I curs = 0; if(a.size() != b.size()) return false; while(curs < a.size() && tolower(a[curs]) == tolower(b[curs])) curs++; return curs >= a.size(); } void tools_to_upper(const string & r, string & uppered) { #if HAVE_WCTYPE_H && HAVE_WCHAR_H try { wstring tmp = tools_string_to_wstring(r); tools_to_wupper(tmp); uppered = tools_wstring_to_string(tmp); } catch(Erange & e) { U_I taille = r.size(); uppered = r; for(U_I x = 0; x < taille; ++x) uppered[x] = toupper(uppered[x]); } #else U_I taille = r.size(); uppered = r; for(U_I x = 0; x < taille; ++x) uppered[x] = toupper(uppered[x]); #endif } #if HAVE_WCTYPE_H void tools_to_wupper(wstring & r) { wstring::iterator it = r.begin(); while(it != r.end()) { *it = towupper(*it); ++it; } } #endif static void ignore_deadson(S_I sig) { signal(SIGCHLD, &ignore_deadson); } static void abort_on_deadson(S_I sig) { // we cannot throw exception in a handler (it would not be caught) we have no other way to report to standard error cerr << dar_gettext("Aborting program: child process died unexpectedly") << endl; } static void runson(user_interaction & dialog, char * const argv[]) { if(execvp(argv[0], argv) < 0) { string tmp = tools_strerror_r(errno); dialog.message(tools_printf(dar_gettext("Error trying to run %s: %s"), argv[0], tmp.c_str())); } else dialog.message(string(dar_gettext("execvp() failed but did not returned error code"))); #ifndef EXIT_ERROR #define EXIT_ERROR 2 exit(EXIT_ERROR); // we need the appropriate dar exit status // but we are in libdar thus cannot include dar_suite.hpp header // we thus copy #undef EXIT_ERROR #else exit(EXIT_ERROR); #endif } string tools_printf(const char *format, ...) { va_list ap; va_start(ap, format); string output = ""; try { output = tools_vprintf(format, ap); } catch(...) { va_end(ap); throw; } va_end(ap); return output; } string tools_vprintf(const char *format, va_list ap) { bool end; U_32 taille = strlen(format)+1; char *copie; string output = ""; U_I test; copie = new (nothrow) char[taille]; if(copie == nullptr) throw Ememory("tools_printf"); try { char *ptr = copie, *start = copie; strncpy(copie, format, taille); copie[taille-1] = '\0'; do { while(*ptr != '%' && *ptr != '\0') ++ptr; if(*ptr == '%') { *ptr = '\0'; end = false; } else end = true; output += start; if(!end) { ++ptr; switch(*ptr) { case '%': output += "%"; break; case 'd': output += tools_int2str(va_arg(ap, S_I)); break; case 'u': test = va_arg(ap, U_I); output += deci(test).human(); break; case 'x': test = va_arg(ap, U_I); output += tools_string_to_hexa(deci(test).human()); break; case 'o': test = va_arg(ap, U_I); output += tools_int2octal(test); break; case 's': output += va_arg(ap, char *); break; case 'c': output += static_cast(va_arg(ap, S_I)); break; case 'i': output += deci(*(va_arg(ap, infinint *))).human(); break; case 'S': output += *(va_arg(ap, string *)); break; default: throw Efeature(tools_printf("%%%c is not implemented in tools_printf format argument", *ptr)); } ++ptr; start = ptr; } } while(!end); } catch(...) { delete [] copie; throw; } delete [] copie; return output; } void tools_unlink_file_mask_regex(user_interaction & dialog, const entrepot & ent, const string & file_mask, bool info_details) { regular_mask my_mask = regular_mask(file_mask, true); path chemin = path(ent.get_url(), true); string entry; ent.read_dir_reset(); while(ent.read_dir_next(entry)) if(my_mask.is_covered(entry)) { const string c_entry = (chemin.append(entry)).display(); if(info_details) dialog.message(tools_printf(dar_gettext("Removing file %s"), c_entry.c_str())); try { ent.unlink(entry); } catch(Euser_abort & e) { throw; } catch(Ebug & e) { throw; } catch(Erange & e) { dialog.message(e.get_message()); } catch(Egeneric & e) { string msg = e.get_message(); dialog.message(tools_printf(dar_gettext("Error removing file %s: %S"), c_entry.c_str(), &msg)); } } } bool tools_do_some_files_match_mask_regex(const entrepot & ent, const string & file_mask) { bool ret = false; regular_mask my_mask = regular_mask(file_mask, true); string entry; ent.read_dir_reset(); while(!ret && ent.read_dir_next(entry)) if(my_mask.is_covered(entry)) ret = true; return ret; } void tools_avoid_slice_overwriting_regex(user_interaction & dialog, const entrepot & ent, const string & basename, const string & extension, bool info_details, bool allow_overwriting, bool warn_overwriting, bool dry_run) { const string c_chemin = ent.get_url(); const string file_mask = string("^") + tools_escape_chars_in_string(basename, "[].+|!*?{}()^$-,\\") + "\\.[0-9]+\\." + extension + "(\\.(md5|sha1|sha512))?$"; if(tools_do_some_files_match_mask_regex(ent, file_mask)) { if(!allow_overwriting) throw Erange("tools_avoid_slice_overwriting", tools_printf(dar_gettext("Overwriting not allowed while a slice of a previous archive with the same basename has been found in the %s directory, Operation aborted"), c_chemin.c_str())); else { try { if(warn_overwriting) dialog.pause(tools_printf(dar_gettext("At least one slice of an old archive with the same name remains in the directory %s. It is advised to remove all the old archive's slices before creating an archive of same name. Can I remove these old slices?"), c_chemin.c_str())); if(!dry_run) tools_unlink_file_mask_regex(dialog, ent, file_mask, info_details); } catch(Euser_abort & e) { // nothing to do, just continue } } } } bool tools_are_on_same_filesystem(const string & file1, const string & file2) { dev_t id; struct stat sstat; if(stat(file1.c_str(), &sstat) < 0) { string tmp = tools_strerror_r(errno); throw Erange("tools:tools_are_on_same_filesystem", tools_printf(dar_gettext("Cannot get inode information for %s: %s"), file1.c_str(), tmp.c_str())); } id = sstat.st_dev; if(stat(file2.c_str(), &sstat) < 0) { string tmp = tools_strerror_r(errno); throw Erange("tools:tools_are_on_same_filesystem", tools_printf(dar_gettext("Cannot get inode information for %s: %s"), file2.c_str(), tmp.c_str())); } return id == sstat.st_dev; } path tools_relative2absolute_path(const path & src, const path & cwd) { if(src.is_relative()) if(cwd.is_relative()) throw Erange("tools_relative2absolute_path", dar_gettext("Current Working Directory cannot be a relative path")); else return cwd + src; else return src; } void tools_block_all_signals(sigset_t &old_mask) { sigset_t all; sigfillset(&all); #if HAVE_LIBPTHREAD if(pthread_sigmask(SIG_BLOCK, &all, &old_mask) != 0) #else if(sigprocmask(SIG_BLOCK, &all, &old_mask) != 0) #endif throw Erange("tools_block_all_signals", string(dar_gettext("Cannot block signals: "))+tools_strerror_r(errno)); } void tools_set_back_blocked_signals(sigset_t old_mask) { #if HAVE_LIBPTHREAD if(pthread_sigmask(SIG_SETMASK, &old_mask, nullptr)) #else if(sigprocmask(SIG_SETMASK, &old_mask, nullptr)) #endif throw Erange("tools_set_back_block_all_signals", string(dar_gettext("Cannot unblock signals: "))+tools_strerror_r(errno)); } U_I tools_count_in_string(const string & s, const char a) { U_I ret = 0, c, size = s.size(); for(c = 0; c < size; ++c) if(s[c] == a) ++ret; return ret; } datetime tools_get_mtime(user_interaction & dialog, const std::string & s, bool auto_zeroing, bool silent, const set & ignored_as_symlink) { struct stat buf; bool use_stat = ignored_as_symlink.find(s) != ignored_as_symlink.end(); int sysval; if(use_stat) sysval = stat(s.c_str(), &buf); else sysval = lstat(s.c_str(), &buf); if(sysval < 0) { string tmp = tools_strerror_r(errno); throw Erange("tools_get_mtime", tools_printf(dar_gettext("Cannot get last modification date: %s"), tmp.c_str())); } #if LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND || LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_NANOSECOND tools_check_negative_date(buf.st_mtim.tv_sec, dialog, s.c_str(), "mtime", auto_zeroing, silent); #if LIBDAR_TIME_READ_ACCURACY == LIBDAR_TIME_ACCURACY_MICROSECOND datetime val = datetime(buf.st_mtim.tv_sec, buf.st_mtim.tv_nsec/1000, datetime::tu_microsecond); #else datetime val = datetime(buf.st_mtim.tv_sec, buf.st_mtim.tv_nsec, datetime::tu_nanosecond); #endif if(val.is_null() && !auto_zeroing) // assuming an error avoids getting time that way val = datetime(buf.st_mtime, 0, datetime::tu_second); #else tools_check_negative_date(buf.st_mtime, dialog, s.c_str(), "mtime", auto_zeroing, silent); datetime val = datetime(buf.st_mtime, 0, datetime::tu_second); #endif return val; } infinint tools_get_size(const std::string & s) { struct stat buf; if(lstat(s.c_str(), &buf) < 0) { string tmp = tools_strerror_r(errno); throw Erange("tools_get_size", tools_printf(dar_gettext("Cannot get last modification date: %s"), tmp.c_str())); } if(!S_ISREG(buf.st_mode)) throw Erange("tools_get_size", tools_printf(dar_gettext("Cannot get size of %S: not a plain file"), &s)); return buf.st_size; } infinint tools_get_extended_size(string s, U_I base) { U_I len = s.size(); infinint factor = 1; if(len < 1) return false; switch(s[len-1]) { case 'K': // 'K' is the Kelvin symbol (temperature unit) though many make the confusion case 'k': // kilobyte the official/correct symbol factor = base; break; case 'M': // megabyte factor = infinint(base).power((U_I)2); break; case 'G': // gigabyte factor = infinint(base).power((U_I)3); break; case 'T': // terabyte factor = infinint(base).power((U_I)4); break; case 'P': // petabyte factor = infinint(base).power((U_I)5); break; case 'E': // exabyte factor = infinint(base).power((U_I)6); break; case 'Z': // zettabyte factor = infinint(base).power((U_I)7); break; case 'Y': // yottabyte factor = infinint(base).power((U_I)8); break; case 'R': factor = infinint(base).power((U_I)9); break; case 'Q': factor = infinint(base).power((U_I)10); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; default : throw Erange("command_line get_extended_size", tools_printf(dar_gettext("Unknown suffix [%c] in string %S"), s[len-1], &s)); } if(factor != 1) s = string(s.begin(), s.end()-1); deci tmp = s; factor *= tmp.computer(); return factor; } string tools_substitute(const string & hook, const map & corres) { string ret = ""; string::iterator it = const_cast(hook).begin(); while(it != hook.end()) { if(*it == '%') { it++; if(it != hook.end()) { map::const_iterator mptr = corres.find(*it); if(mptr == corres.end()) throw Escript("tools_substitute", string(dar_gettext("Unknown substitution string: %")) + *it); else ret += mptr->second; it++; } else // reached end of "hook" string { throw Escript("tools_substitute", dar_gettext("last char of user command-line to execute is '%', (use '%%' instead to avoid this message)")); } } else { ret += *it; it++; } } return ret; } string tools_hook_substitute(const string & hook, const string & path, const string & basename, const string & num, const string & padded_num, const string & ext, const string & context, const string & base_url) { map corres; corres['%'] = "%"; corres['p'] = path; corres['b'] = basename; corres['n'] = num; corres['N'] = padded_num; corres['e'] = ext; corres['c'] = context; corres['u'] = base_url; return tools_substitute(hook, corres); } void tools_hook_execute(user_interaction & ui, const string & cmd_line) { NLS_SWAP_IN; try { const char *ptr = cmd_line.c_str(); bool loop = false; do { try { S_I code = system(ptr); switch(code) { case 0: loop = false; break; // All is fine, script did not report error case 127: throw Erange("tools_hook_execute", gettext("execve() failed. (process table is full ?)")); case -1: throw Erange("tools_hook_execute", string(gettext("system() call failed: ")) + tools_strerror_r(errno)); default: throw Erange("tools_hook_execute", tools_printf(gettext("execution of [ %S ] returned error code: %d"), &cmd_line, code)); } } catch(Erange & e) { try { ui.pause(string(gettext("Error during user command line execution: ")) + e.get_message() + gettext(" . Retry command-line ?")); loop = true; } catch(Euser_abort & f) { ui.pause(gettext("Ignore previous error on user command line and continue ?")); loop = false; } } } while(loop); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } extern void tools_hook_substitute_and_execute(user_interaction & ui, const string & hook, const string & path, const string & basename, const string & num, const string & padded_num, const string & ext, const string & context, const string & base_url) { string cmd_line; cmd_line = tools_hook_substitute(hook, path, basename, num, padded_num, ext, context, base_url); try { tools_hook_execute(ui, cmd_line); } catch(Euser_abort & g) { throw Escript("sar::hook_execute", string(dar_gettext("Fatal error on user command line: ")) + g.get_message()); } } /*************************************************************/ string tools_output2xml(const string & src) { string ret = ""; U_I cur = 0, size = src.size(); while(cur < size) { switch(src[cur]) { case '<': ret += "<"; break; case '>': ret += ">"; break; case '&': ret += "&"; break; case '\'': ret += "'"; break; case'\"': ret += """; break; default: ret += src[cur]; } ++cur; } return ret; } U_I tools_octal2int(const std::string & perm) { U_I len = perm.size(); U_I ret = 0; enum { init , octal , trail, error } etat = init; if(perm == "") return 0666; // permission used by default (compatible with dar's previous behavior) for(U_I i = 0; i < len ; i++) switch(etat) { case init: switch(perm[i]) { case ' ': case '\t': case '\n': case '\r': break; case '0': etat = octal; break; default: etat = error; break; } break; case octal: if(perm[i] == ' ') etat = trail; else if(perm[i] >= '0' && perm[i] <= '7') ret = ret*8 + perm[i] - '0'; else etat = error; break; case trail: if(perm[i] != ' ') etat = error; break; case error: throw Erange("tools_octal2int", dar_gettext("Badly formated octal number")); default: throw SRC_BUG; } if(etat == error || etat == init) throw Erange("tools_octal2int", dar_gettext("Badly formated octal number")); return ret; } string tools_int2octal(const U_I & perm) { deque digits = tools_number_base_decomposition_in_big_endian(perm, (U_I)8); deque::iterator it = digits.begin(); string ret = ""; while(it != digits.end()) { string tmp; tmp += '0' + (*it); ret = tmp + ret; ++it; } return string("0") + ret; // leading zero for octal format indication } char tools_cast_type_to_unix_type(char type) { char ret = type; if(type == 'f') // plain files ret = '-'; if(type == 'o') // door "files" ret = 'D'; return ret; } string tools_get_permission_string(char type, U_32 perm, bool hard) { string ret = hard ? "*" : " "; ret += tools_cast_type_to_unix_type(type); if((perm & 0400) != 0) ret += 'r'; else ret += '-'; if((perm & 0200) != 0) ret += 'w'; else ret += '-'; if((perm & 0100) != 0) if((perm & 04000) != 0) ret += 's'; else ret += 'x'; else if((perm & 04000) != 0) ret += 'S'; else ret += '-'; if((perm & 040) != 0) ret += 'r'; else ret += '-'; if((perm & 020) != 0) ret += 'w'; else ret += '-'; if((perm & 010) != 0) if((perm & 02000) != 0) ret += 's'; else ret += 'x'; else if((perm & 02000) != 0) ret += 'S'; else ret += '-'; if((perm & 04) != 0) ret += 'r'; else ret += '-'; if((perm & 02) != 0) ret += 'w'; else ret += '-'; if((perm & 01) != 0) if((perm & 01000) != 0) ret += 't'; else ret += 'x'; else if((perm & 01000) != 0) ret += 'T'; else ret += '-'; return ret; } U_I tools_get_permission(S_I fd) { struct stat buf; int err = fstat(fd, &buf); if(err < 0) throw Erange("tools_get_permission", string(gettext("Cannot get effective permission given a file descriptor: ")) + tools_strerror_r(errno)); return buf.st_mode & ~(S_IFMT); } void tools_set_permission(S_I fd, U_I perm) { NLS_SWAP_IN; try { if(fd < 0) throw SRC_BUG; if(fchmod(fd, (mode_t) perm) < 0) { string tmp = tools_strerror_r(errno); throw Erange("tools_set_permission", tools_printf(gettext("Error while setting file permission: %s"), tmp.c_str())); } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } uid_t tools_ownership2uid(const string & user) { uid_t ret = -1; NLS_SWAP_IN; try { bool direct_uid_set = false; if(user.empty()) throw Erange("tools_ownership2uid", gettext("An empty string is not a valid user name")); try { ret = tools_str2int(user); direct_uid_set = true; } catch(Erange & e) { // the given user is not an uid } if(!direct_uid_set) { #ifdef __DYNAMIC__ const char *c_user = user.c_str(); #if HAVE_GETPWNAM_R struct passwd puser; struct passwd *result; S_I size = sysconf(_SC_GETPW_R_SIZE_MAX); char *buf = nullptr; if(size == -1) size = 16384; try { buf = new (nothrow) char[size]; if(buf == nullptr) throw Ememory("tools_ownership2uid"); int val = getpwnam_r(c_user, &puser, buf, size, &result); if(val != 0 || result == nullptr) { string err = val == 0 ? gettext("Unknown user") : tools_strerror_r(errno); throw Erange("tools_ownership2uid", tools_printf(gettext("Error found while looking for UID of user %s: %S"), c_user, &err)); } ret = result->pw_uid; } catch(...) { if(buf != nullptr) delete [] buf; throw; } if(buf != nullptr) delete [] buf; #else errno = 0; struct passwd *puser = getpwnam(c_user); if(puser == nullptr) { string err = (errno == 0) ? gettext("Unknown user") : tools_strerror_r(errno); throw Erange("tools_ownership2uid", tools_printf(gettext("Error found while looking for UID of user %s: %S"), c_user, &err)); } ret = puser->pw_uid; #endif #else throw Erange("tools_ownership2uid", dar_gettext("Cannot convert username to uid in statically linked binary, either directly provide the UID or run libdar from a dynamically linked executable")); #endif } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } gid_t tools_ownership2gid(const string & group) { gid_t ret = -1; NLS_SWAP_IN; try { bool direct_gid_set = false; if(group.empty()) throw Erange("tools_ownership2gid", gettext("An empty string is not a valid group name")); try { ret = tools_str2int(group); direct_gid_set = true; } catch(Erange & e) { // the given group is not an gid } if(!direct_gid_set) { #ifdef __DYNAMIC__ const char *c_group = group.c_str(); #if HAVE_GETGRNAM_R struct group pgroup; struct group *result; U_I size = sysconf(_SC_GETGR_R_SIZE_MAX); char *buf = nullptr; try { buf = new (nothrow) char[size]; if(buf == nullptr) throw Ememory("tools_ownsership2gid"); S_I val = getgrnam_r(c_group, &pgroup, buf, size, &result); if(val != 0 || result == nullptr) { string err = (val == 0) ? gettext("Unknown group") : tools_strerror_r(errno); throw Erange("tools_ownership2gid", tools_printf(gettext("Error found while looking fo GID of group %s: %S"), c_group, &err)); } ret = result->gr_gid; } catch(...) { if(buf != nullptr) delete [] buf; throw; } if(buf != nullptr) delete [] buf; #else errno = 0; struct group *pgroup = getgrnam(c_group); if(pgroup == nullptr) { string err = (errno == 0) ? gettext("Unknown group") : tools_strerror_r(errno); throw Erange("tools_ownership2gid", tools_printf(gettext("Error found while looking for GID of group %s: %S"), c_group, &err)); } ret = pgroup->gr_gid; #endif #else throw Erange("tools_ownership2gid", dar_gettext("Cannot convert username to uid in statically linked binary, either directly provide the UID or run libdar from a dynamically linked executable")); #endif } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return ret; } void tools_set_ownership(int filedesc, const std::string & user, const std::string & group) { uid_t uid = -1; uid_t gid = -1; if(user != "") uid = tools_ownership2uid(user); if(group != "") gid = tools_ownership2gid(group); if(uid != (uid_t)(-1) || gid != (gid_t)(-1)) { if(fchown(filedesc, uid, gid) < 0) { string tmp = tools_strerror_r(errno); throw Erange("tools_set_ownership", tools_printf(gettext("Error while setting file user ownership: %s"), tmp.c_str())); } } } void tools_memxor(void *dest, const void *src, U_I n) { unsigned char *d = (unsigned char *) dest; const unsigned char *s = (const unsigned char *) src; for(U_I i = 0; i < n; i++) *d++ ^= *s++; } tlv_list tools_string2tlv_list(user_interaction & dialog, const U_16 & type, const vector & data) { vector::const_iterator it = data.begin(); tlv tmp; tlv_list ret; tmp.set_type(type); while(it != data.end()) { tmp.reset(); tmp.write(it->c_str(), it->size()); ret.add(tmp); it++; } return ret; } U_I tools_pseudo_random(U_I max) { return (U_I)(max*((float)(rand())/RAND_MAX)); } string tools_unsigned_char_to_hexa(unsigned char x) { string ret; deque digit = tools_number_base_decomposition_in_big_endian(x, (U_I)(16)); deque::reverse_iterator itr = digit.rbegin(); switch(digit.size()) { case 0: ret = "00"; break; case 1: ret = "0"; break; case 2: break; default: throw SRC_BUG; } while(itr != digit.rend()) { U_I t = *itr; if(t > 9) ret += ('a' + (t - 10)); else ret += ('0' + t); ++itr; } return ret; } string tools_string_to_hexa(const string & input) { string::const_iterator it = input.begin(); string ret; while(it != input.end()) { ret += tools_unsigned_char_to_hexa((unsigned char)(*it)); ++it; } return ret; } infinint tools_file_size_to_crc_size(const infinint & size) { const infinint ratio = tools_get_extended_size("1G",1024); infinint r; infinint crc_size; if(!size.is_zero()) { euclide(size, ratio, crc_size, r); if(!r.is_zero()) ++crc_size; crc_size *= 4; // smallest value is 4 bytes, 4 bytes more per each additional 1 Gbyte of data } else crc_size = 1; // minimal value for no data to protect by checksum return crc_size; } string tools_getcwd() { const U_I step = 1024; U_I length = step; char *buffer = nullptr, *ret; string cwd; try { do { buffer = new (nothrow) char[length]; if(buffer == nullptr) throw Ememory("line_tools_getcwd()"); ret = getcwd(buffer, length-1); // length-1 to keep a place for ending '\0' if(ret == nullptr) // could not get the CWD { if(errno == ERANGE) // buffer too small { delete [] buffer; buffer = nullptr; length += step; } else // other error throw Erange("line_tools_getcwd", string(dar_gettext("Cannot get full path of current working directory: ")) + tools_strerror_r(errno)); } } while(ret == nullptr); buffer[length - 1] = '\0'; cwd = buffer; } catch(...) { if(buffer != nullptr) delete [] buffer; throw; } if(buffer != nullptr) delete [] buffer; return cwd; } string tools_get_compression_ratio(const infinint & storage_size, const infinint & file_size, bool compressed) { static const char * not_compressed = " "; if(!compressed) return not_compressed; else if(file_size >= storage_size) if(!file_size.is_zero()) return tools_addspacebefore(deci(((file_size - storage_size)*100)/file_size).human(), 4) +"%"; else return not_compressed; else return gettext("Worse"); } #define MSGSIZE 200 string tools_strerror_r(int errnum) { char buffer[MSGSIZE]; string ret; #ifdef HAVE_STRERROR_R #ifdef HAVE_STRERROR_R_CHAR_PTR char *val = strerror_r(errnum, buffer, MSGSIZE); if(val != buffer) strncpy(buffer, val, MSGSIZE); #else // we expect the XSI-compliant strerror_r int val = strerror_r(errnum, buffer, MSGSIZE); if(val != 0) { string tmp = tools_printf(gettext("Error code %d to message conversion failed"), errnum); strncpy(buffer, tmp.c_str(), tools_min((size_t)(tmp.size()+1), (size_t)(MSGSIZE))); } #endif #else char *tmp = strerror(errnum); (void)strncpy(buffer, tmp, MSGSIZE); #endif buffer[MSGSIZE-1] = '\0'; ret = buffer; return ret; } #ifdef GPGME_SUPPORT string tools_gpgme_strerror_r(gpgme_error_t err) { char buffer[MSGSIZE]; string ret; switch(gpgme_strerror_r(err, buffer, MSGSIZE)) { case 0: break; case ERANGE: strncpy(buffer, "Lack of memory to display gpgme error message", MSGSIZE); break; default: throw SRC_BUG; } buffer[MSGSIZE-1] = '\0'; ret = buffer; return ret; } #endif #if HAVE_WCHAR_H wstring tools_string_to_wstring(const string & val) { wstring ret; wchar_t *dst = new (nothrow) wchar_t[val.size() + 1]; if(dst == nullptr) throw Ememory("tools_string_to_wcs"); try { mbstate_t state_wc; const char *src = val.c_str(); size_t len; memset(&state_wc, '\0', sizeof(state_wc)); // initializing the shift structure len = mbsrtowcs(dst, &src, val.size(), &state_wc); if(len == (size_t)-1) throw Erange("tools_string_to_wcs", string(gettext("Invalid wide-char found in string: ")) + tools_strerror_r(errno)); dst[len] = '\0'; // converting dst to wstring ret = dst; } catch(...) { if(dst != nullptr) delete [] dst; throw; } if(dst != nullptr) delete [] dst; return ret; } string tools_wstring_to_string(const wstring & val) { string ret; const wchar_t *src = val.c_str(); mbstate_t state_wc; size_t len; memset(&state_wc, '\0', sizeof(state_wc)); // initializing the shift structure len = wcsrtombs(nullptr, &src, 0, &state_wc); if(len == (size_t)-1) throw Erange("tools_wstring_to_string", string(gettext("Invalid wide-char found in string:")) + tools_strerror_r(errno)); char *dst = new (nothrow) char[len + 1]; if(dst == nullptr) throw Ememory("tools_wstring_to_string"); try { size_t len2; memset(&state_wc, '\0', sizeof(state_wc)); // initializing the shift structure src = val.c_str(); len2 = wcsrtombs(dst, &src, len, &state_wc); if(len != len2) throw SRC_BUG; if(len2 == (size_t)-1) throw SRC_BUG; // problem should have already raised above dst[len2] = '\0'; // converting dst to string ret = dst; } catch(...) { if(dst != nullptr) delete [] dst; throw; } if(dst != nullptr) delete [] dst; return ret; } #endif void tools_secu_string_show(user_interaction & dialog, const string & msg, const secu_string & key) { string res = msg + tools_printf(" (size=%d) [", key.get_size()); U_I max = key.get_size() - 1; for(U_I index = 0; index < max; ++index) res += tools_printf(" %d |", key[index]); res += tools_printf(" %d ]", key[max]); dialog.message(res); } void tools_unlink(const std::string & filename) { U_I ret = unlink(filename.c_str()); if(ret != 0) { string err = tools_strerror_r(errno); throw Erange("tools_unlink", tools_printf(gettext("Error unlinking %S: %S"), &filename, &err)); } } string tools_escape_chars_in_string(const string & val, const char *to_escape) { string ret; string::const_iterator it = val.begin(); while(it != val.end()) { U_I curs = 0; while(to_escape[curs] != '\0' && to_escape[curs] != *it) ++curs; if(to_escape[curs] != '\0') ret += "\\"; ret += *it; ++it; } return ret; } bool tools_infinint2U_64(infinint val, U_64 & res) { res = 0; val.unstack(res); return val.is_zero(); } bool tools_my_atoi(const char *a, U_I & val) { try { val = tools_str2int(a); return true; } catch(Erange & e) { val = 0; return false; } } infinint tools_double2infinint(double arg) { if(arg < 0) throw Erange("tools_double2infinint", gettext("Cannot convert negative floating point value to unsigned (positive) integer")); U_I tmp = (U_I)arg; if(arg - (double)tmp > 0.5) ++tmp; return infinint(tmp); } infinint tools_upper_rounded_log2(const infinint & ref) { infinint ret = 0; infinint tmp = ref; while(!tmp.is_zero()) { tmp >>= 1; ++ret; } return ret; } infinint tools_lower_rounded_exp2(const infinint & ref) { return infinint(1) << ref; } infinint tools_rounded_square_root(const infinint & ref) { return tools_lower_rounded_exp2(tools_upper_rounded_log2(ref) / 2); } infinint tools_rounded_cube_root(const infinint & ref) { return tools_lower_rounded_exp2(tools_upper_rounded_log2(ref) / 3); } } // end of namespace dar-2.7.15/src/libdar/terminateur.cpp0000644000175000017500000001061414636066467014410 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "terminateur.hpp" #include "elastic.hpp" #define BLOCK_SIZE 4 namespace libdar { void terminateur::dump(generic_file & f) { infinint size = f.get_position(), nbbit, reste; S_I last_byte; unsigned char a; pos.dump(f); size = f.get_position() - size; euclide(size, BLOCK_SIZE, nbbit, reste); if(!reste.is_zero()) { // adding some non informational bytes to get a multiple of BLOCK_SIZE S_I bourrage = reste % BLOCK_SIZE; a = 0; for(S_I i = bourrage; i < BLOCK_SIZE; ++i) f.write((char *)&a, 1); // one more for remaing bytes and non informational bytes. ++nbbit; } last_byte = nbbit % 8; nbbit /= 8; // now, nbbit is the number of byte of terminator string (more or less 1) if(last_byte != 0) { // making the last byte (starting eof) of the terminator string a = 0; for(S_I i = 0; i < last_byte; ++i) { a >>= 1; a |= 0x80; } f.write((char *)&a, 1); } else // adding a terminal non 0xFF byte. (terminal when read down from end of file) { a = 0; f.write((char *)&a, 1); } // writing down all the other bytes of the terminator string a = 0xff; while(!nbbit.is_zero()) { f.write((char *)&a, 1); --nbbit; } } void terminateur::read_catalogue(generic_file & f, bool with_elastic, const archive_version & reading_ver, const infinint & where_from) { S_I offset = 0; unsigned char a; if(where_from.is_zero()) f.skip_to_eof(); else f.skip(where_from); if(with_elastic) (void)elastic(f, elastic_backward, reading_ver); // temporary anomymous elastic skip backward in 'f' // up to the other elastic buffer end try { // reading & counting the terminator string char b; do { if(f.read_back(b) != 1) throw Erange("",""); // exception used locally a = (unsigned char)b; if(a == 0xFF) ++offset; } while(a == 0xFF); offset *= 8; // offset is now a number of bits // considering the first non 0xFF byte of the terminator string (backward reading) while(a != 0) { if((a & 0x80) == 0) throw Erange("",""); ++offset; a <<= 1; } offset *= BLOCK_SIZE; // offset is now the byte offset of the position start // now we know where is located the position structure pointing to the start of the catalogue if(offset < 0) throw SRC_BUG; // signed int overflow // skipping the start of "location" if(! f.skip_relative(-offset)) throw Erange("",""); t_start = f.get_position(); } catch(Erange &e) { throw Erange("terminateur::get_catalogue", gettext("Badly formatted terminator, cannot extract catalogue location: ") + e.get_message()); } // reading and returning the position pos = infinint(f); } } // end of namespace dar-2.7.15/src/libdar/generic_rsync.cpp0000644000175000017500000002575214636066467014714 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_LIBRSYNC_H #include #include #endif #if HAVE_STRING_H #include #endif } // end extern "C" #include "generic_rsync.hpp" #include "memory_file.hpp" #include "null_file.hpp" #include "erreurs.hpp" #define BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFFER_SIZE #undef BUFFER_SIZE #define BUFFER_SIZE SSIZE_MAX #endif #endif #define SMALL_BUF 10 using namespace std; namespace libdar { // signature creation generic_rsync::generic_rsync(generic_file *signature_storage, U_I signature_block_size, generic_file *below): generic_file(gf_read_only) { #if LIBRSYNC_AVAILABLE // sanity checks if(signature_storage == nullptr) throw SRC_BUG; if(below == nullptr) throw SRC_BUG; // setting up the object working_buffer = new (nothrow) char[BUFFER_SIZE]; if(working_buffer == nullptr) throw Ememory("generic_rsync::generic_rsync (sign)"); try { working_size = 0; status = sign; x_below = below; x_output = signature_storage; x_input = nullptr; sumset = nullptr; initial = true; patching_completed = false; // not used in sign mode data_crc = nullptr; #ifdef RS_DEFAULT_STRONG_LEN job = rs_sig_begin(signature_block_size, RS_DEFAULT_STRONG_LEN); #else // should use RS_BLAKE2_SIG_MAGIC in place of RS_MD4_SIG_MAGIC // but not compatible with librsync < 1.0 job = rs_sig_begin(signature_block_size, 0, RS_MD4_SIG_MAGIC); #endif } catch(...) { delete [] working_buffer; throw; } #else throw Ecompilation("librsync support"); #endif } // delta creation generic_rsync::generic_rsync(generic_file *base_signature, generic_file *below, const infinint & crc_size, const crc **checksum): generic_file(gf_read_only) { #if LIBRSYNC_AVAILABLE char *inbuf = nullptr; char *outbuf = nullptr; U_I lu = 0; U_I out; bool eof = false; rs_result err; // sanity checks if(base_signature == nullptr) throw SRC_BUG; if(below == nullptr) throw SRC_BUG; // setting up the object working_size = 0; status = delta; initial = true; patching_completed = false; // not used in delta mode data_crc = nullptr; working_buffer = new (nothrow) char[BUFFER_SIZE]; if(working_buffer == nullptr) throw Ememory("generic_rsync::generic_rsync (sign)"); try { // loading signature into memory job = rs_loadsig_begin(&sumset); try { inbuf = new (nothrow) char[BUFFER_SIZE]; outbuf = new (nothrow) char [SMALL_BUF]; // nothing should be output if(inbuf == nullptr || outbuf == nullptr) throw Ememory("generic_rsync::generic_rsync (delta)"); base_signature->skip(0); do { lu += base_signature->read(inbuf + lu, BUFFER_SIZE - lu); if(lu == 0) eof = true; out = SMALL_BUF; if(!step_forward(inbuf, lu, true, outbuf, out) && eof) throw SRC_BUG; if(out != 0) throw SRC_BUG; // loading signature into memory // should never produce data on output } while(!eof); if(inbuf != nullptr) { delete [] inbuf; inbuf = nullptr; } if(outbuf != nullptr) { delete [] outbuf; outbuf = nullptr; } free_job(); } catch(...) { if(inbuf != nullptr) { delete [] inbuf; inbuf = nullptr; } if(outbuf != nullptr) { delete [] outbuf; outbuf = nullptr; } free_job(); rs_free_sumset(sumset); throw; } // creating the delta job if(checksum != nullptr) data_crc = create_crc_from_size(crc_size); if(data_crc == nullptr) throw Ememory("generic_rsync::generic_rsync"); try { err = rs_build_hash_table(sumset); if(err != RS_DONE) throw Erange("generic_rsync::generic_rsync", string(gettext("Error met building the rsync hash table: ")) + string(rs_strerror(err))); job = rs_delta_begin(sumset); x_below = below; x_input = nullptr; x_output = nullptr; } catch(...) { if(data_crc != nullptr) delete data_crc; throw; } if(data_crc != nullptr) *checksum = data_crc; } catch(...) { delete [] working_buffer; throw; } #else throw Ecompilation("librsync support"); #endif } // patch creation generic_rsync::generic_rsync(generic_file *current_data, generic_file *delta): generic_file(gf_read_only) { #if LIBRSYNC_AVAILABLE // sanity checks if(current_data == nullptr) throw SRC_BUG; if(delta == nullptr) throw SRC_BUG; // setting up the object status = patch; patching_completed = false; x_input = current_data; x_output = nullptr; x_below = delta; sumset = nullptr; initial = true; working_size = 0; data_crc = nullptr; working_buffer = new (nothrow) char[BUFFER_SIZE]; if(working_buffer == nullptr) throw Ememory("generic_rsync::generic_rsync (sign)"); try { job = rs_patch_begin(generic_rsync::patch_callback, this); } catch(...) { delete [] working_buffer; throw; } #else throw Ecompilation("librsync support"); #endif } generic_rsync::~generic_rsync() noexcept(false) { terminate(); delete [] working_buffer; } U_I generic_rsync::inherited_read(char *a, U_I size) { U_I lu = 0; U_I remain; bool eof = false; initial = false; if(patching_completed) return 0; switch(status) { case sign: lu = x_below->read(a, size); remain = lu; do { working_size = BUFFER_SIZE; (void)step_forward(a + lu - remain, remain, false, working_buffer, working_size); if(working_size > 0) x_output->write(working_buffer, working_size); } while(remain > 0); break; case delta: do { if(!eof) { U_I tmp = x_below->read(working_buffer + working_size, BUFFER_SIZE - working_size); if(tmp > 0) { if(data_crc != nullptr) data_crc->compute(working_buffer + working_size, tmp); working_size += tmp; } if(working_size == 0) eof = true; } else working_size = 0; remain = size - lu; (void)step_forward(working_buffer, working_size, true, a + lu, remain); lu += remain; } while(lu < size && (!eof || remain != 0)); break; case patch: do { if(!eof) { working_size += x_below->read(working_buffer + working_size, BUFFER_SIZE - working_size); if(working_size == 0) eof = true; } else working_size = 0; remain = size - lu; if(step_forward(working_buffer, working_size, true, a + lu, remain)) { if(working_size > 0 && remain == 0) throw Edata("While patching file, librsync tells it has finished processing data while we still have pending data to send to it"); patching_completed = true; } else { if(eof && remain == 0) throw Edata("While patching file, librsync tells it has not finished processing data while we have no more to feed to it and librsync did not made any progression in the last cycle (it did not produce new data)"); } lu += remain; } while(lu < size && (remain > 0 || !eof) && !patching_completed); break; default: throw SRC_BUG; } return lu; } void generic_rsync::inherited_write(const char *a, U_I size) { initial = false; switch(status) { case sign: throw SRC_BUG; case delta: throw SRC_BUG; case patch: throw SRC_BUG; default: throw SRC_BUG; } } void generic_rsync::inherited_terminate() { switch(status) { case sign: case delta: send_eof(); break; case patch: break; default: throw SRC_BUG; } #if LIBRSYNC_AVAILABLE if(sumset != nullptr) { rs_free_sumset(sumset); sumset = nullptr; } #endif free_job(); } #if LIBRSYNC_AVAILABLE rs_result generic_rsync::patch_callback(void *opaque, rs_long_t pos, size_t *len, void **buf) { rs_result ret; generic_rsync *me = (generic_rsync *)(opaque); U_I lu; if(me == nullptr) throw SRC_BUG; if(me->x_input == nullptr) throw SRC_BUG; try { me->x_input->skip(pos); lu = me->x_input->read((char *)*buf, *len); if(*len > 0 && lu == 0) ret = RS_INPUT_ENDED; else ret = RS_DONE; *len = lu; } catch(...) { *len = 0; ret = RS_IO_ERROR; } return ret; } #endif bool generic_rsync::step_forward(const char *buffer_in, U_I & avail_in, bool shift_input, char *buffer_out, U_I & avail_out) { #if LIBRSYNC_AVAILABLE bool ret; rs_buffers_t buf; rs_result res; buf.next_in = const_cast(buffer_in); buf.avail_in = avail_in; buf.next_out = buffer_out; buf.avail_out = avail_out; if(avail_in == 0) // EOF reached when called with no input bytes buf.eof_in = 1; else buf.eof_in = 0; res = rs_job_iter(job, &buf); switch(res) { case RS_DONE: ret = true; break; case RS_BLOCKED: ret = false; break; default: throw Erange("generic_rsync::step_forward", string(gettext("Error met while feeding data to librsync: ")) + rs_strerror(res)); } if(buf.avail_in > 0 && shift_input) (void)memmove(const_cast(buffer_in), buf.next_in, buf.avail_in); avail_in = buf.avail_in; avail_out = buf.next_out - buffer_out; return ret; #else return false; #endif } void generic_rsync::free_job() { #if LIBRSYNC_AVAILABLE if(job != nullptr) { rs_result err = rs_job_free(job); job = nullptr; if(err != RS_DONE) throw Erange("generic_rsync::inherited_terminate", string(gettext("Error releasing librsync job: ")) + string(rs_strerror(err))); } #endif } void generic_rsync::send_eof() { U_I tmp; bool finished; do { tmp = 0; working_size = BUFFER_SIZE; finished = step_forward(working_buffer, tmp, // as tmp is set to zero we can use working_buffer in input too true, working_buffer, working_size); if(working_size > 0) x_output->write(working_buffer, working_size); if(tmp > 0) throw SRC_BUG; } while(working_size > 0 && !finished); } } // end of namespace dar-2.7.15/src/libdar/mask_list.cpp0000644000175000017500000002254114636067146014034 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_STRING_H #include #endif } // end extern "C" #include "mask_list.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "cygwin_adapt.hpp" #include "fichier_local.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { static bool modified_lexicalorder_a_lessthan_b(const std::string & a, const std::string & b); mask_list::mask_list(const string & filename_list_st, bool case_sensit, const path & prefix_t, bool include) { NLS_SWAP_IN; try { try { case_s = case_sensit; //< object's field including = include; //< object's field fichier_local source = filename_list_st; //< where we read data from char *buffer = nullptr; //< hold the just read data static const U_I buf_size = 20480; //< size of buffer: we read at most this number of bytes at a time list tmp; //< list of all raw lines read, without any prefix U_I lu = 0, curs; //< cursor used as cisors to split data in line char *beg = nullptr; //< points to the beginning of the next line inside buffer, when more than one line can be found in buffer string str_beg; //< holds the std::string copy of beg, eventually uppercased string current_entry = ""; //< holds the current line converted to string between each read() path prefix = prefix_t; //< the prefix to add to relative paths ///////////// // changing the prefix to uppercase if case sensitivity is disabled if(!case_sensit) { string ptp = prefix_t.display(); string upper; tools_to_upper(ptp, upper); prefix = path(upper); } ///////////// // building buffer that will be used to split read data line by line buffer = new (nothrow) char[buf_size+1]; // one char more to be able to add a '\0' if necessary if(buffer == nullptr) throw Erange("mask_list::mask_list", tools_printf(gettext("Cannot allocate memory for buffer while reading %S"), &filename_list_st)); ///////////// // filling 'tmp' with with each line read try { do { lu = source.read(buffer, buf_size); if(lu > 0) { curs = 0; beg = buffer; do { while(curs < lu && buffer[curs] != '\n' && buffer[curs] != '\0') curs++; if(curs < lu) { if(buffer[curs] == '\0') throw Erange("mask_list::mask_list", tools_printf(gettext("Found '\0' character in %S, not a plain file, aborting"), &filename_list_st)); if(buffer[curs] == '\n') { buffer[curs] = '\0'; if(!case_s) tools_to_upper(beg, str_beg); else str_beg = string(beg); current_entry += str_beg; if(current_entry != "") tmp.push_back(current_entry); current_entry = ""; curs++; beg = buffer + curs; } else throw SRC_BUG; } else // reached end of buffer { if(lu == buf_size && beg == buffer) { buffer[buf_size - 1] = '\0'; throw Erange("mask_list::mask_list", tools_printf(gettext("line exceeding the maximum of %d characters in listing file %S, aborting. Concerned line starts with: %s"), buf_size - 1, &filename_list_st, buffer)); } buffer[lu] = '\0'; if(!case_s) tools_to_upper(beg, str_beg); else str_beg = string(beg); current_entry += str_beg; } } while(curs < lu); } } while(lu > 0); if(current_entry != "") tmp.push_back(current_entry); } catch(...) { delete [] buffer; throw; } delete [] buffer; buffer = nullptr; ///////////// // - completing relative paths of the list // - removing the ending part of the possible ending \r when DOS formatting is used // if(prefix.is_relative() && !prefix.is_subdir_of(path(""), true)) throw Erange("mask_list::mask_list", gettext("Mask_list's prefix must be an absolute path or start with \"\" string for archive merging")); else { path current("/"); list ::iterator it = tmp.begin(); while(it != tmp.end()) { try { // checking for trailing \r if(it->size() < 1) throw SRC_BUG; // we should not have empty string in the list if((*it)[it->size() - 1] == '\r') // last char of the string is a '\r' { it->erase(it->size() - 1, 1); // removing the last char (thus removing \r here) if(it->empty()) { it = tmp.erase(it); // removing the string if "it" got empty // and having "it" pointing to the next string in the list continue; // and looping back } } // adding prefix path current = *it; if(current.is_relative()) { current = prefix + current; *it = current.display(); } } catch(Egeneric & e) { string err = e.get_message(); string line = *it; throw Erange("mask_list::mask_list", tools_printf(gettext("Error met while reading line\n\t%S\n from file %S: %S"), &line, &filename_list_st, &err)); } it++; } } ///////////// // sorting the list of entry // sorting the list with a modified lexicographical order where the / as is lowest character, other letter order unchanged tmp.sort(&modified_lexicalorder_a_lessthan_b); tmp.unique(); // remove duplicates // converting the sorted list to deque, to get the indexing feature of this type contenu.assign(tmp.begin(), tmp.end()); taille = contenu.size(); if(taille < contenu.size()) throw Erange("mask_list::mask_list", tools_printf(gettext("Too much line in file %S (integer overflow)"), &filename_list_st)); } catch(Egeneric & e) { e.prepend_message(tools_printf(gettext("Error met while opening %S: "), &filename_list_st)); throw; } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } bool mask_list::is_covered(const string & expression) const { if(taille == 0) return false; U_I min = 0, max = taille-1, tmp; string target; bool ret; if(case_s) target = expression; else tools_to_upper(expression, target); // divide & conquer algorithm on a sorted list (aka binary search) while(max - min > 1) { tmp = (min + max)/2; if(modified_lexicalorder_a_lessthan_b(contenu[tmp], target)) min = tmp; else max = tmp; // we could set max to tmp-1 but we need to get min < target < max , if target is absent from the list } if(min == 0 && modified_lexicalorder_a_lessthan_b(target, contenu[min])) max = min; ret = contenu[max] == target || contenu[min] == target; if(including && !ret) // if including files, we must also include directories leading to a listed file { string c_max = contenu[max]; ret = path(c_max).is_subdir_of(expression, case_s); } return ret; } string mask_list::dump(const string & prefix) const { deque::const_iterator it = contenu.begin(); string rec_pref = prefix + " | "; string ret = prefix + "If matches one of the following line(s):\n"; while(it != contenu.end()) { ret += rec_pref + *it + "\n"; ++it; } ret += prefix + " +--"; return ret; } static bool modified_lexicalorder_a_lessthan_b(const string & a, const string & b) { string::const_iterator at = a.begin(); string::const_iterator bt = b.begin(); while(at != a.end() && bt != b.end()) { if(*at == '/') { if(*bt != '/') return true; // else both a and b current letter are equal to '/' // reading further } else { if(*bt == '/') return false; else { if(*at != *bt) return *at < *bt; // else a and b letter are equal // reading further to find a difference } } ++at; ++bt; } if(at == a.end()) return true; // even if bt == b.end(), we have a <= b if(bt == b.end()) return false; // because at != a.end(), we thus have neither a == b nor a < b else throw SRC_BUG; // at != a.end() and bt != b.end() how did we escaped the while loop? } } // end of namespace dar-2.7.15/src/libdar/entrepot_libcurl5.hpp0000644000175000017500000000633414636067146015516 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file entrepot_libcurl5.hpp /// \brief defines the implementation for remote filesystem entrepot using libcurl /// \ingroup API #ifndef ENTREPOT_LIBCURL5_HPP #define ENTREPOT_LIBCURL5_HPP #include "../my_config.h" extern "C" { } #include #include #include "entrepot_libcurl.hpp" #include "user_interaction5.hpp" namespace libdar5 { /// \addtogroup Private /// @{ /// implementation for entrepot to access to local filesystem /// entrepot_local generates objects of class "fichier_local" inherited class of fichier_global using libdar::mycurl_protocol; class entrepot_libcurl : public libdar::entrepot_libcurl { public: entrepot_libcurl(user_interaction & dialog, ///< for user interaction mycurl_protocol proto, ///< network protocol to use const std::string & login, ///< user login on remote host const secu_string & password, ///< user password on remote host (empty for file auth or user interaction) const std::string & host, ///< the remote server to connect to const std::string & port, ///< TCP/UDP port to connec to (empty string for default) bool auth_from_file, ///< whether to check $HOME/.netrc for password const std::string & sftp_pub_keyfile, ///< where to fetch the public key (sftp only) const std::string & sftp_prv_keyfile, ///< where to fetch the private key (sftp only) const std::string & sftp_known_hosts, ///< location of the known_hosts file (empty string to disable this security check) U_I waiting_time): libdar::entrepot_libcurl(user_interaction5_clone_to_shared_ptr(dialog), proto, login, password, host, port, auth_from_file, sftp_pub_keyfile, sftp_prv_keyfile, sftp_known_hosts, false, waiting_time) {} entrepot_libcurl(const entrepot_libcurl & ref) = default; entrepot_libcurl(entrepot_libcurl && ref) noexcept = default; entrepot_libcurl & operator = (const entrepot_libcurl & ref) = default; entrepot_libcurl & operator = (entrepot_libcurl && ref) noexcept = default; ~entrepot_libcurl() throw () {}; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/criterium.hpp0000644000175000017500000006076614636066467014076 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file criterium.hpp /// \brief contains classes that let the user define the policy for overwriting files /// \ingroup API #ifndef CRITERIUM_HPP #define CRITERIUM_HPP #include "../my_config.h" #include #include "erreurs.hpp" #include "infinint.hpp" namespace libdar { /// \addtogroup API /// @{ /// no need ot dig into class cat_nomme here class cat_nomme; /// the generic criterium class, parent of all criterium /// this is a pure virtual class that is used in API call /// it is used to federate under a single type all the /// criterium classes defined below. It defines a common /// interface for all of them. class criterium { public: criterium() {}; criterium(const criterium & ref) = default; criterium(criterium && ref) noexcept = default; criterium & operator = (const criterium & ref) = default; criterium & operator = (criterium && ref) noexcept = default; virtual ~criterium() noexcept(false) {}; /// criterum interface method /// \param[in] first entry to compare with the following (this is the original or 'in place' entry) /// \param[in] second the other entry to compare with the previous one (this is the new entry to add) /// \return the result of the criterium evaluation (true or false) virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const = 0; /// clone construction method /// \return a new object of the same type, /// \note this method must be implemented in all the leaf classes of the /// class hierarchy rooted at the criterium class virtual criterium *clone() const = 0; }; // ////////////////////////////////////////////////////////// // ////////// a set of criterium classes follows //////////// // ////////////////////////////////////////////////////////// /// returns true if the first entry is an inode (whatever is the second) /// \note the current only entry that can be found in an archive which is not an inode, is an entry /// signaling that a file has been destroyed since the archive of reference. class crit_in_place_is_inode : public criterium { public: crit_in_place_is_inode() {}; crit_in_place_is_inode(const crit_in_place_is_inode & ref) = default; crit_in_place_is_inode(crit_in_place_is_inode && ref) noexcept = default; crit_in_place_is_inode & operator = (const crit_in_place_is_inode & ref) = default; crit_in_place_is_inode & operator = (crit_in_place_is_inode && ref) noexcept = default; ~crit_in_place_is_inode() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_is_inode(*this); }; }; /// returns true if the first entry is a cat_directory (whatever is the second) class crit_in_place_is_dir : public criterium { public: crit_in_place_is_dir() {}; crit_in_place_is_dir(const crit_in_place_is_dir & ref) = default; crit_in_place_is_dir(crit_in_place_is_dir && ref) noexcept = default; crit_in_place_is_dir & operator = (const crit_in_place_is_dir & ref) = default; crit_in_place_is_dir & operator = (crit_in_place_is_dir && ref) noexcept = default; ~crit_in_place_is_dir() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_is_dir(*this); }; }; /// returns true if the first entry is a plain file (whatever is the second) class crit_in_place_is_file : public criterium { public: crit_in_place_is_file() {}; crit_in_place_is_file(const crit_in_place_is_file & ref) = default; crit_in_place_is_file(crit_in_place_is_file && ref) noexcept = default; crit_in_place_is_file & operator = (const crit_in_place_is_file & ref) = default; crit_in_place_is_file & operator = (crit_in_place_is_file && ref) noexcept = default; ~crit_in_place_is_file() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_is_file(*this); }; }; /// returns true if the first entry is a inode with several hard links (whatever is the second entry) /// it may be a plain file, a symlink a char device, a block device or a named pipe for example class crit_in_place_is_hardlinked_inode : public criterium { public: crit_in_place_is_hardlinked_inode() {}; crit_in_place_is_hardlinked_inode(const crit_in_place_is_hardlinked_inode & ref) = default; crit_in_place_is_hardlinked_inode(crit_in_place_is_hardlinked_inode && ref) noexcept = default; crit_in_place_is_hardlinked_inode & operator = (const crit_in_place_is_hardlinked_inode & ref) = default; crit_in_place_is_hardlinked_inode & operator = (crit_in_place_is_hardlinked_inode && ref) noexcept = default; ~crit_in_place_is_hardlinked_inode() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_is_hardlinked_inode(*this); }; }; /// returns true if the first entry is a inode with several hard links /// (whatever is the second entry) and also if this first entry is the first /// we meet that points to this hard linked inode class crit_in_place_is_new_hardlinked_inode : public criterium { public: crit_in_place_is_new_hardlinked_inode() {}; crit_in_place_is_new_hardlinked_inode(const crit_in_place_is_new_hardlinked_inode & ref) = default; crit_in_place_is_new_hardlinked_inode(crit_in_place_is_new_hardlinked_inode && ref) noexcept = default; crit_in_place_is_new_hardlinked_inode & operator = (const crit_in_place_is_new_hardlinked_inode & ref) = default; crit_in_place_is_new_hardlinked_inode & operator = (crit_in_place_is_new_hardlinked_inode && ref) noexcept = default; ~crit_in_place_is_new_hardlinked_inode() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_is_new_hardlinked_inode(*this); }; }; /// returns true if the data of the first entry is more recent or of the same date of the one of the second entry /// this class always returns false if both entry are not inode. Comparison is done on mtime class crit_in_place_data_more_recent : public criterium { public: crit_in_place_data_more_recent(const infinint & hourshift = 0) : x_hourshift(hourshift) {}; crit_in_place_data_more_recent(const crit_in_place_data_more_recent & ref) = default; crit_in_place_data_more_recent(crit_in_place_data_more_recent && ref) noexcept = default; crit_in_place_data_more_recent & operator = (const crit_in_place_data_more_recent & ref) = default; crit_in_place_data_more_recent & operator = (crit_in_place_data_more_recent && ref) noexcept = default; ~crit_in_place_data_more_recent() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_data_more_recent(*this); }; private: infinint x_hourshift; }; /// returns true if the data of the first entry is more recent or of the same date as the fixed /// date given in argument to the constructor /// If the in_place entry is not an inode its date is considered equal to zero. Comparison is done on mtime class crit_in_place_data_more_recent_or_equal_to : public criterium { public: crit_in_place_data_more_recent_or_equal_to(const infinint & date, const infinint & hourshift = 0) : x_hourshift(hourshift), x_date(date) {}; crit_in_place_data_more_recent_or_equal_to(const crit_in_place_data_more_recent_or_equal_to & ref) = default; crit_in_place_data_more_recent_or_equal_to(crit_in_place_data_more_recent_or_equal_to && ref) noexcept = default; crit_in_place_data_more_recent_or_equal_to & operator = (const crit_in_place_data_more_recent_or_equal_to & ref) = default; crit_in_place_data_more_recent_or_equal_to & operator = (crit_in_place_data_more_recent_or_equal_to && ref) noexcept = default; ~crit_in_place_data_more_recent_or_equal_to() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_data_more_recent_or_equal_to(*this); }; private: infinint x_hourshift; infinint x_date; }; /// returns true if the data of the first entry is bigger or equal to the one of the second entry /// this class always returns false if both entries are not plain files class crit_in_place_data_bigger : public criterium { public: crit_in_place_data_bigger() {}; crit_in_place_data_bigger(const crit_in_place_data_bigger & ref) = default; crit_in_place_data_bigger(crit_in_place_data_bigger && ref) noexcept = default; crit_in_place_data_bigger & operator = (const crit_in_place_data_bigger & ref) = default; crit_in_place_data_bigger & operator = (crit_in_place_data_bigger && ref) noexcept = default; ~crit_in_place_data_bigger() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_data_bigger(*this); }; }; /// returns true if the data of the first entry is saved int the archive /// (not marked as unchanged since the archive of reference) /// if the entry is not an inode the result is also true class crit_in_place_data_saved : public criterium { public: crit_in_place_data_saved() {}; crit_in_place_data_saved(const crit_in_place_data_saved & ref) = default; crit_in_place_data_saved(crit_in_place_data_saved && ref) noexcept = default; crit_in_place_data_saved & operator = (const crit_in_place_data_saved & ref) = default; crit_in_place_data_saved & operator = (crit_in_place_data_saved && ref) noexcept = default; ~crit_in_place_data_saved() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_data_saved(*this); }; }; /// return true if the entry is a dirty file (or hard linked dirty file) class crit_in_place_data_dirty : public criterium { public: crit_in_place_data_dirty() {}; crit_in_place_data_dirty(const crit_in_place_data_dirty & ref) = default; crit_in_place_data_dirty(crit_in_place_data_dirty && ref) noexcept = default; crit_in_place_data_dirty & operator = (const crit_in_place_data_dirty & ref) = default; crit_in_place_data_dirty & operator = (crit_in_place_data_dirty && ref) noexcept = default; ~crit_in_place_data_dirty() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_data_dirty(*this); }; }; /// return true if the entry is a sparse file (or hard linked sparse file) class crit_in_place_data_sparse : public criterium { public: crit_in_place_data_sparse() {}; crit_in_place_data_sparse(const crit_in_place_data_sparse & ref) = default; crit_in_place_data_sparse(crit_in_place_data_sparse && ref) noexcept = default; crit_in_place_data_sparse & operator = (const crit_in_place_data_sparse & ref) = default; crit_in_place_data_sparse & operator = (crit_in_place_data_sparse && ref) noexcept = default; ~crit_in_place_data_sparse() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_data_sparse(*this); }; }; /// return true if the entry has delta signature class crit_in_place_has_delta_sig : public criterium { public: crit_in_place_has_delta_sig() {}; crit_in_place_has_delta_sig(const crit_in_place_has_delta_sig & ref) = default; crit_in_place_has_delta_sig(crit_in_place_has_delta_sig && ref) noexcept = default; crit_in_place_has_delta_sig & operator = (const crit_in_place_has_delta_sig & ref) = default; crit_in_place_has_delta_sig & operator = (crit_in_place_has_delta_sig && ref) noexcept = default; ~crit_in_place_has_delta_sig() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_has_delta_sig(*this); }; }; /// returns true if both inputs are inode of the same type (file/pipe/device/...) and share inode information /// inode information taken into account is ///. the permission, ///. ownership ///. modification date ///. file size (for plain files or hard linked files) ///. major and minor (for block and char devices) ///. target (for symlinks) /// /// But no comparison is done on atime and mtime. class crit_same_inode_data : public criterium { public: crit_same_inode_data() {}; crit_same_inode_data(const crit_same_inode_data & ref) = default; crit_same_inode_data(crit_same_inode_data && ref) noexcept = default; crit_same_inode_data & operator = (const crit_same_inode_data & ref) = default; crit_same_inode_data & operator = (crit_same_inode_data && ref) noexcept = default; ~crit_same_inode_data() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_same_inode_data(*this); }; }; /// returns true if the first entry is first an inode, and has some EA (EA may be saved /// or just recorded as existing). class crit_in_place_EA_present : public criterium { public: crit_in_place_EA_present() {}; crit_in_place_EA_present(const crit_in_place_EA_present & ref) = default; crit_in_place_EA_present(crit_in_place_EA_present && ref) noexcept = default; crit_in_place_EA_present & operator = (const crit_in_place_EA_present & ref) = default; crit_in_place_EA_present & operator = (crit_in_place_EA_present && ref) noexcept = default; ~crit_in_place_EA_present() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_EA_present(*this); }; }; /// returns true if the EA of the first entry is more recent or equal to the one of the second entry /// if no EA are present in 'to be added' or if it even not an inode true is returned. If 'in place' /// does not have EA or is even not an inode true is returned unless 'to be added' has EA present. /// \note that the comparison is done on the ctime, EA may be just marked as saved in the archive of /// reference or be saved in the current archive, this does not have any impact on the comparison. class crit_in_place_EA_more_recent : public criterium { public: crit_in_place_EA_more_recent(const infinint & hourshift = 0) : x_hourshift(hourshift) {}; crit_in_place_EA_more_recent(const crit_in_place_EA_more_recent & ref) = default; crit_in_place_EA_more_recent(crit_in_place_EA_more_recent && ref) noexcept = default; crit_in_place_EA_more_recent & operator = (const crit_in_place_EA_more_recent & ref) = default; crit_in_place_EA_more_recent & operator = (crit_in_place_EA_more_recent && ref) noexcept = default; ~crit_in_place_EA_more_recent() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_EA_more_recent(*this); }; private: infinint x_hourshift; }; /// returns true if the EA of the first entry is more recent or equal to the fixed date given in argument to the constructor /// comparison using ctime of the "in place" object. If no ctime is available (not an inode for example) /// the date is considered equal to zero. class crit_in_place_EA_more_recent_or_equal_to : public criterium { public: crit_in_place_EA_more_recent_or_equal_to(const infinint & date, const infinint & hourshift = 0) : x_hourshift(hourshift), x_date(date) {}; crit_in_place_EA_more_recent_or_equal_to(const crit_in_place_EA_more_recent_or_equal_to & ref) = default; crit_in_place_EA_more_recent_or_equal_to(crit_in_place_EA_more_recent_or_equal_to && ref) noexcept = default; crit_in_place_EA_more_recent_or_equal_to & operator = (const crit_in_place_EA_more_recent_or_equal_to & ref) = default; crit_in_place_EA_more_recent_or_equal_to & operator = (crit_in_place_EA_more_recent_or_equal_to && ref) noexcept = default; ~crit_in_place_EA_more_recent_or_equal_to() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_EA_more_recent_or_equal_to(*this); }; private: infinint x_hourshift; infinint x_date; }; /// returns true if the first entry has more or even EA (in number not in size) than the second entry /// if an entry is not an inode or has no EA it is assumed it has zero EA, the comparison is done on that number. class crit_in_place_more_EA : public criterium { public: crit_in_place_more_EA() {}; crit_in_place_more_EA(const crit_in_place_more_EA & ref) = default; crit_in_place_more_EA(crit_in_place_more_EA && ref) noexcept = default; crit_in_place_more_EA & operator = (const crit_in_place_more_EA & ref) = default; crit_in_place_more_EA & operator = (crit_in_place_more_EA && ref) noexcept = default; ~crit_in_place_more_EA() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_more_EA(*this); }; }; /// returns true if the space used by EA of the first entry is greater or equal to the space used by the EA of the second entry (no EA means 0 byte for EA storage) /// this criterium does not have any consideration for the second entry class crit_in_place_EA_bigger : public crit_in_place_more_EA { public: crit_in_place_EA_bigger() {}; crit_in_place_EA_bigger(const crit_in_place_EA_bigger & ref) = default; crit_in_place_EA_bigger(crit_in_place_EA_bigger && ref) noexcept = default; crit_in_place_EA_bigger & operator = (const crit_in_place_EA_bigger & ref) = default; crit_in_place_EA_bigger & operator = (crit_in_place_EA_bigger && ref) noexcept = default; ~crit_in_place_EA_bigger() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_EA_bigger(*this); }; }; /// returns true if the in place entry has its EA saved (not just marked as saved) in the archve of reference /// this criterium does not have any consideration for the second entry class crit_in_place_EA_saved : public criterium { public: crit_in_place_EA_saved() {}; crit_in_place_EA_saved(const crit_in_place_EA_saved & ref) = default; crit_in_place_EA_saved(crit_in_place_EA_saved && ref) noexcept = default; crit_in_place_EA_saved & operator = (const crit_in_place_EA_saved & ref) = default; crit_in_place_EA_saved & operator = (crit_in_place_EA_saved && ref) noexcept = default; ~crit_in_place_EA_saved() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_in_place_EA_saved(*this); }; }; /// returns true if the two entries are of the same type (plain-file/char dev/block dev/named pipe/symlink/directory/unix socket) /// two plain files are considered of same type even if one is hard linked while the other is not /// same thing whether one entry has EA while the other has not, they are still considered of the same type. class crit_same_type : public criterium { public: crit_same_type() {}; crit_same_type(const crit_same_type & ref) = default; crit_same_type(crit_same_type && ref) noexcept = default; crit_same_type & operator = (const crit_same_type & ref) = default; crit_same_type & operator = (crit_same_type && ref) noexcept = default; ~crit_same_type() = default; virtual bool evaluate(const cat_nomme &first, const cat_nomme &second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_same_type(*this); }; }; /// realises the negation of the criterium given in argument to its constructor class crit_not : public criterium { public: crit_not(const criterium & crit) { x_crit = crit.clone(); if(x_crit == nullptr) throw Ememory("crit_not::crit_not"); }; crit_not(const crit_not & ref): criterium(ref) { copy_from(ref); }; crit_not(crit_not && ref) noexcept: criterium(std::move(ref)) { x_crit = nullptr; std::swap(x_crit, ref.x_crit); }; crit_not & operator = (const crit_not & ref) { destroy(); copy_from(ref); return *this; }; crit_not & operator = (crit_not && ref) noexcept { criterium::operator = (std::move(ref)); std::swap(x_crit, ref.x_crit); return *this; }; ~crit_not() { destroy(); }; virtual bool evaluate(const cat_nomme & first, const cat_nomme & second) const override { return ! x_crit->evaluate(first, second); }; virtual criterium *clone() const override { return new (std::nothrow) crit_not(*this); }; protected: const criterium *x_crit; private: void copy_from(const crit_not & ref); void destroy() { if(x_crit != nullptr) { delete x_crit; x_crit = nullptr; } }; }; /// realises the *AND* operator class crit_and : public criterium { public: crit_and() { clear(); }; crit_and(const crit_and & ref) : criterium(ref) { copy_from(ref); }; crit_and(crit_and && ref) = default; // moving the deque while the pointed to object are untouched crit_and & operator = (const crit_and & ref) { detruit(); copy_from(ref); return *this; }; crit_and & operator = (crit_and && ref) noexcept = default; // moving the deque while the pointed to object are untouched ~crit_and() { detruit(); }; void add_crit(const criterium & ref); void clear() { detruit(); }; /// this call merges to the current call the arguments of another "crit_and", the given argument is cleared of its arguments. void gobe(crit_and & to_be_voided); virtual bool evaluate(const cat_nomme & first, const cat_nomme & second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_and(*this); }; protected: std::deque operand; private: void copy_from(const crit_and & ref); void detruit(); }; class crit_or : public crit_and { public: crit_or() { clear(); }; crit_or(const crit_or & ref) = default; crit_or(crit_or && ref) = default; crit_or & operator = (const crit_or & ref) = default; crit_or & operator = (crit_or && ref) noexcept = default; ~crit_or() = default; virtual bool evaluate(const cat_nomme & first, const cat_nomme & second) const override; virtual criterium *clone() const override { return new (std::nothrow) crit_or(*this); }; }; class crit_invert : public crit_not { public: crit_invert(const criterium & crit) : crit_not(crit) {}; crit_invert(const crit_invert & ref) = default; crit_invert(crit_invert && ref) noexcept = default; crit_invert & operator = (const crit_invert & ref) = default; crit_invert & operator = (crit_invert && ref) noexcept = default; ~crit_invert() = default; virtual bool evaluate(const cat_nomme & first, const cat_nomme & second) const override { return x_crit->evaluate(second, first); }; virtual criterium *clone() const override { return new (std::nothrow) crit_invert(*this); }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/macro_tools.cpp0000644000175000017500000021267014636067146014373 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STDLIB_H #include #endif #if HAVE_GCRYPT_H #ifndef GCRYPT_NO_DEPRECATED #define GCRYPT_NO_DEPRECATED #endif #include #endif } // end extern "C" #include "macro_tools.hpp" #include "terminateur.hpp" #include "user_interaction.hpp" #include "zapette.hpp" #include "sar.hpp" #include "elastic.hpp" #include "tronc.hpp" #include "trontextual.hpp" #include "thread_cancellation.hpp" #include "deci.hpp" #include "escape_catalogue.hpp" #include "tronc.hpp" #include "cache.hpp" #include "null_file.hpp" #include "secu_memory_file.hpp" #include "generic_to_global_file.hpp" #include "tlv.hpp" #include "crypto_sym.hpp" #include "tronconneuse.hpp" #include "crypto_asym.hpp" #include "cat_all_entrees.hpp" #include "crc.hpp" #include "entrepot_libcurl.hpp" #include "scrambler.hpp" #include "hash_fichier.hpp" #include "tools.hpp" #include "compressor.hpp" #include "compressor_zstd.hpp" #include "lz4_module.hpp" #include "gzip_module.hpp" #include "bzip2_module.hpp" #include "xz_module.hpp" #include "lzo_module.hpp" #include "zstd_module.hpp" #include "block_compressor.hpp" #ifdef LIBTHREADAR_AVAILABLE #include "parallel_block_compressor.hpp" #include "parallel_tronconneuse.hpp" #endif #define PRE_2_7_0_LZO_BLOCK_SIZE 246660 using namespace std; namespace libdar { const string LIBDAR_STACK_LABEL_CACHE_PIPE = "CACHE_PIPE"; const string LIBDAR_STACK_LABEL_UNCOMPRESSED = "UNCOMPRESSED"; const string LIBDAR_STACK_LABEL_CLEAR = "CLEAR"; const string LIBDAR_STACK_LABEL_UNCYPHERED = "UNCYPHERED"; const string LIBDAR_STACK_LABEL_LEVEL1 = "LEVEL1"; /// this is the archive version format generated by the application /// this is also the highest version of format that can be read const archive_version macro_tools_supported_version = archive_version(11,3); static void version_check(user_interaction & dialog, const header_version & ver); /// append an elastic buffer of given size to the file /// \param[in,out] f file to append elastic buffer to /// \param[in] max_size size of the elastic buffer to add /// \param[in] modulo defines the size to choose (see note) /// \param[in] offset defines the offset to apply (see note) /// \note the size of the elastic buffer should not exceed max_size but /// should be chosen in order to reach a size which is zero modulo "modulo" /// assuming the offset we add the elastic buffer at is "offset". If modulo is zero /// this the elastic buffer is randomly chosen from 1 to max_size without any /// concern about being congruent to a given modulo. /// Example if module is 5 and offset is 2, the elastic buffer possible size /// can be 3 (2+3 is congruent to 0 modulo 5), 8 (2+8 is congruent to modulo 5), 12, etc. /// but not exceed max_size+modulo-1 /// \note this is to accomodate the case when encrypted data is followed by clear data /// at the end of an archive. There is no way to known when we read clear data, but we /// know the clear data size is very inferior to crypted block size, thus when reading /// a uncompleted block of data we can be sure we have reached and of file and that /// the data is clear without any encrypted part because else we would have read an entire /// block of data. static void macro_tools_add_elastic_buffer(generic_file & f, U_32 max_size, U_32 modulo, U_32 offset); /// create a compress_module based on the provided arguments static unique_ptr make_compress_module_ptr(compression algo, U_I compression_level = 9); catalogue *macro_tools_get_catalogue_from(const shared_ptr & dialog, pile & stack, const header_version & ver, bool info_details, infinint &cat_size, const infinint & second_terminateur_offset, list & signatories, bool lax_mode) { return macro_tools_get_derivated_catalogue_from(dialog, stack, stack, ver, info_details, cat_size, second_terminateur_offset, signatories, lax_mode); } catalogue *macro_tools_get_derivated_catalogue_from(const shared_ptr & dialog, pile & data_stack, pile & cata_stack, const header_version & ver, bool info_details, infinint &cat_size, const infinint & second_terminateur_offset, list & signatories, bool lax_mode) { terminateur term; catalogue *ret = nullptr; pile_descriptor data_pdesc(&data_stack); pile_descriptor cata_pdesc(&cata_stack); generic_file *crypto = cata_stack.get_by_label(LIBDAR_STACK_LABEL_UNCYPHERED); contextual *data_ctxt = nullptr; contextual *cata_ctxt = nullptr; if(!dialog) throw SRC_BUG; // dialog points to nothing signatories.clear(); data_stack.find_first_from_top(data_ctxt); if(data_ctxt == nullptr) throw SRC_BUG; cata_stack.find_first_from_top(cata_ctxt); if(cata_ctxt == nullptr) throw SRC_BUG; if(info_details) dialog->message(gettext("Locating archive contents...")); if(ver.get_edition() > 3) term.read_catalogue(*crypto, ver.is_ciphered(), ver.get_edition(), 0); // terminator is encrypted since format "04" // elastic buffer present when encryption is used else term.read_catalogue(*crypto, false, ver.get_edition()); // elastic buffer did not exist before format "04" cata_stack.flush_read_above(crypto); if(info_details) dialog->message(gettext("Reading archive contents...")); if(cata_stack.skip(term.get_catalogue_start())) { if(term.get_catalogue_start() > term.get_terminateur_start()) throw SRC_BUG; cat_size = term.get_terminateur_start() - term.get_catalogue_start(); ret = macro_tools_read_catalogue(dialog, ver, cata_pdesc, cat_size, signatories, lax_mode, label_zero, false); // only_detruit if(ret == nullptr) throw Ememory("get_catalogue_from"); // the following is necessary in multi-threaded context // where the data_ctxt is a tuyau object: // both read data and orders (to change the context value) // use the same channel // and having this thread changing the contextual // info without first stopping subthreads would // conflict the thread that may still be reading ahead // data from the archive data_stack.flush_read_above(dynamic_cast(data_ctxt)); cata_stack.flush_read_above(dynamic_cast(cata_ctxt)); try { data_ctxt->set_info_status(CONTEXT_OP); cata_ctxt->set_info_status(CONTEXT_OP); if(&cata_stack != &data_stack) ret->change_location(data_pdesc); } catch(...) { if(ret != nullptr) delete ret; throw; } } else throw Erange("get_catalogue_from", gettext("Missing catalogue in file.")); return ret; } catalogue *macro_tools_read_catalogue(const shared_ptr & dialog, const header_version & ver, const pile_descriptor & cata_pdesc, const infinint & cat_size, list & signatories, bool lax_mode, const label & lax_layer1_data_name, bool only_detruits) { catalogue *ret = nullptr; memory_file hash_to_compare; hash_fichier *hasher = nullptr; signatories.clear(); cata_pdesc.check(false); try // release hasher in case of exception { if(ver.is_signed()) { generic_to_global_file *global_hash_to_compare = nullptr; generic_to_global_file *global_cata_top_stack = nullptr; try { global_hash_to_compare = new (nothrow) generic_to_global_file(dialog, &hash_to_compare, gf_write_only); if(global_hash_to_compare == nullptr) throw Ememory("macro_tools_get_derivated_catalogue_from"); global_cata_top_stack = new (nothrow) generic_to_global_file(dialog, cata_pdesc.stack->top(), gf_read_only); if(global_cata_top_stack == nullptr) throw Ememory("macro_tools_get_derivated_catalogue_from"); hasher = new (nothrow) hash_fichier(dialog, global_cata_top_stack, "x", global_hash_to_compare, hash_algo::sha512); if(hasher == nullptr) throw Ememory("macro_tools_get_derivated_catalogue_from"); // at this stage, hasher is created // and manages the objects global_cata_stack and global_hash_to_compare } catch(...) { if(global_hash_to_compare != nullptr) delete global_hash_to_compare; if(global_cata_top_stack != nullptr) delete global_cata_top_stack; throw; } cata_pdesc.stack->push(hasher); } try // trap cast and rethrow exceptions { if(!cat_size.is_zero()) cata_pdesc.stack->read_ahead(cat_size); ret = new (nothrow) catalogue(dialog, cata_pdesc, ver.get_edition(), ver.get_compression_algo(), lax_mode, lax_layer1_data_name, only_detruits); if(ret == nullptr) throw Ememory("macro_tools_read_catalogue"); try { if(hasher != nullptr) { hasher->terminate(); if(cata_pdesc.stack->top() != hasher) throw SRC_BUG; if(cata_pdesc.stack->pop() != hasher) throw SRC_BUG; } if(ver.is_signed()) { tlv hash_to_decrypt(*cata_pdesc.stack); // read the encrypted hash following the catalogue memory_file clear_read_hash; crypto_asym engine(dialog); crc *tmp = nullptr; hash_to_decrypt.skip(0); engine.decrypt(hash_to_decrypt, clear_read_hash); signatories = engine.verify(); if(clear_read_hash.diff(hash_to_compare, 0, 0, 1, tmp)) // difference! { if(lax_mode) dialog->message(gettext("LAX MODE: catalogue computed hash does not match the signed hash of the archive, ignoring")); else throw Edata(gettext("Catalogue computed hash does not match the signed hash of the archive, archive has been modified since it was signed!")); } else { if(tmp != nullptr) delete tmp; // else no difference, // the caller has the signatories and will compare those with the list contained in the archive header } } } catch(...) { if(ret != nullptr) delete ret; throw; } } catch(Ebug & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Egeneric & e) { throw Erange("get_catalogue_from", string(gettext("Cannot open catalogue: ")) + e.get_message()); } } catch(...) { if(cata_pdesc.stack->top() == hasher) { if(cata_pdesc.stack->pop() != hasher) throw SRC_BUG; } if(hasher != nullptr) delete hasher; throw; } if(hasher != nullptr) delete hasher; return ret; } void macro_tools_open_archive(const shared_ptr & dialog, const shared_ptr & where, const string &basename, const infinint & min_digits, const string &extension, crypto_algo crypto, const secu_string & pass, U_32 crypto_size, pile & stack, header_version &ver, const string &input_pipe, const string &output_pipe, const string & execute, infinint & second_terminateur_offset, bool lax, bool has_external_cat, bool sequential_read, bool info_details, list & gnupg_signed, slice_layout & sl, U_I multi_threaded_crypto, U_I multi_threaded_compress, bool header_only) { secu_string real_pass = pass; generic_file *tmp = nullptr; contextual *tmp_ctxt = nullptr; cache *tmp_cache = nullptr; #ifdef LIBCURL_AVAILABLE bool libcurl_repo = dynamic_cast(where.get()) != nullptr; #else bool libcurl_repo = false; #endif string salt; if(!dialog) throw SRC_BUG; // dialog points to nothing stack.clear(); #ifdef LIBTHREADAR_AVAILABLE if(multi_threaded_crypto < 2 && multi_threaded_compress < 2 && !libcurl_repo) stack.ignore_read_ahead(true); else stack.ignore_read_ahead(false); #else stack.ignore_read_ahead(true); #endif sl.first_size = 0; sl.other_size = 0; // we will change that only if sar object is used try { // ****** Building the sar/tuyau/null layer aka level 1 ******* // if(basename == "-") { if(sequential_read) { if(input_pipe == "") { if(info_details) dialog->message(gettext("Opening standard input to read the archive...")); tmp = new (nothrow) trivial_sar(dialog, basename, lax); } else { if(info_details) dialog->printf(gettext("Opening named pipe %S as input to read the archive..."), &input_pipe); tmp = new (nothrow) trivial_sar(dialog, input_pipe, lax); } } else { tuyau *in = nullptr; tuyau *out = nullptr; try { dialog->printf(gettext("Opening a pair of pipes to read the archive, expecting dar_slave at the other ends...")); macro_tools_open_pipes(dialog, input_pipe, output_pipe, in, out); tmp = new (nothrow) zapette(dialog, in, out, true); if(tmp == nullptr) { delete in; in = nullptr; delete out; out = nullptr; } else { in = out = nullptr; // now managed by the zapette tmp->skip_to_eof(); // not sequential reading mode we must skip at eof } } catch(...) { if(in != nullptr) delete in; if(out != nullptr) delete out; throw; } } } else { sar *tmp_sar = nullptr; if(info_details) dialog->message(gettext("Opening the archive using the multi-slice abstraction layer...")); tmp = tmp_sar = new (nothrow) sar(dialog, basename, extension, where, !sequential_read, // not openned by the end in sequential read mode min_digits, sequential_read, lax, execute); if(tmp_sar != nullptr) sl = tmp_sar->get_slicing(); } if(tmp == nullptr) throw Ememory("open_archive"); else { // we always ignore read_ahead as no slave thread will exist for LEVEL1 layer // except for libcurl which can leverage it tmp->ignore_read_ahead(!libcurl_repo); stack.push(tmp, LIBDAR_STACK_LABEL_LEVEL1); tmp = nullptr; } // ****** Reading the header version ************** // stack.find_first_from_top(tmp_ctxt); if(tmp_ctxt == nullptr) throw SRC_BUG; second_terminateur_offset = 0; if(info_details) { if(sequential_read || (tmp_ctxt != nullptr && tmp_ctxt->is_an_old_start_end_archive())) dialog->message(gettext("Reading the archive header...")); else dialog->message(gettext("Reading the archive trailer...")); } if(sequential_read || tmp_ctxt->is_an_old_start_end_archive() || stack.get_position().is_zero()) //< sar layer failed openning the last slice and fallen back openning the first slice stack.skip(0); else { terminateur term; try { term.read_catalogue(stack, false, macro_tools_supported_version); stack.skip(term.get_catalogue_start()); second_terminateur_offset = term.get_catalogue_start(); } catch(Erange & e) { dialog->printf(gettext("Error while reading archive's header, this may be because this archive is an old encrypted archive or that data corruption took place, Assuming it is an old archive, we have to read the header at the beginning of the first slice...")); stack.skip(0); } } ver.read(stack, *dialog, lax); if(second_terminateur_offset.is_zero() && !sequential_read && ver.get_edition() > 7) if(!has_external_cat) { if(!lax) throw Erange("macro_tools_open_archive",gettext("Found a correct archive header at the beginning of the archive, which does not stands to be an old archive, the end of the archive is corrupted and thus the catalogue is not readable, aborting. Either retry providing in addition an isolated catalogue of that archive to perform the operation, or try reading the archive in sequential mode or try in lax mode or, last chance, try both lax and sequential read mode at the same time")); else dialog->pause(gettext("Found a correct archive header at the beginning of the archive, which does not stands to be an old archive, the end of the archive is thus corrupted. Without external catalogue provided and as we do not read the archive in sequential mode, there is very little chance to retreive something from this corrupted archive. Do we continue anyway ?")); } if(header_only) return; // ************* adding a tronc to hide last terminator and trailer_version ******* // if(!second_terminateur_offset.is_zero()) { if(info_details) dialog->printf(gettext("Opening construction layer...")); tmp = new (nothrow) tronc(stack.top(), 0, second_terminateur_offset, false); if(tmp == nullptr) throw Ememory("macro_tools_open_archive"); else { tmp->ignore_read_ahead(!libcurl_repo); // no slave thread used below in the stack stack.clear_label(LIBDAR_STACK_LABEL_LEVEL1); stack.push(tmp, LIBDAR_STACK_LABEL_LEVEL1); tmp = nullptr; } } // ************* building the encryption layer if necessary ************** // if(info_details) dialog->message(gettext("Considering cyphering layer...")); if(ver.is_ciphered() && ver.get_edition() >= 9 && crypto == crypto_algo::none && ver.get_sym_crypto_algo() != crypto_algo::none) { try { if(lax) dialog->pause("LAX MODE: Do we use the encryption algorithm stored in the archive header/trailer?"); crypto = ver.get_sym_crypto_algo(); // using the crypto algorithm recorded in the archive } catch(Euser_abort &) { crypto = crypto_algo::none; } } if(ver.is_ciphered() && crypto == crypto_algo::none) { if(lax) { dialog->message(gettext("LAX MODE: Archive is flagged as being ciphered, assuming data corruption occurred and considering the archive is not ciphered")); } else throw Erange("macro_tools_open_archive", tools_printf(gettext("The archive %S is encrypted and no encryption cipher has been given, cannot open archive."), &basename)); } if(ver.get_crypted_key() != nullptr) // we will find the passphrase from the header's encrypted key { // detemining the size of the unencrypted key infinint i_size = ver.get_crypted_key()->size(); U_I size = 0; i_size.unstack(size); if(!i_size.is_zero()) throw SRC_BUG; // unciphering the encrypted key using GnuPG user's keyring, asking for passphrase if necessary secu_memory_file clear_key = secu_memory_file(size); crypto_asym engine(dialog); ver.get_crypted_key()->skip(0); clear_key.skip(0); engine.decrypt(*(ver.get_crypted_key()), clear_key); gnupg_signed = engine.verify(); // substitution of the pass by the clear_key if decrypt succeeded (else it throws an exception) real_pass = clear_key.get_contents(); } if(crypto != crypto_algo::none && real_pass == "") { if(!secu_string::is_string_secured()) dialog->message(gettext("WARNING: support for secure memory was not available at compilation time, in case of heavy memory load, this may lead the password you are about to provide to be wrote to disk (swap space) in clear. You have been warned!")); real_pass = dialog->get_secu_string(tools_printf(gettext("Archive %S requires a password: "), &basename), false); } switch(crypto) { case crypto_algo::none: if(!ver.get_tape_marks()) { if(info_details) dialog->message(gettext("No cyphering layer opened, adding cache layer for better performance")); // adding the cache layer only if no escape layer will tape place // over. escape layer act a bit like a cache, making caching here useless tmp = tmp_cache = new (nothrow) cache (*(stack.top()), false); if(tmp == nullptr) dialog->message(gettext("Failed opening the cache layer, lack of memory, archive read performances will not be optimized")); } else { if(info_details) dialog->message(gettext("No cyphering layer opened")); } break; case crypto_algo::blowfish: case crypto_algo::aes256: case crypto_algo::twofish256: case crypto_algo::serpent256: case crypto_algo::camellia256: if(info_details) dialog->message(gettext("Opening cyphering layer...")); #ifdef LIBDAR_NO_OPTIMIZATION tools_secu_string_show(*dialog, string("Used clear key: "), real_pass); #endif try { proto_tronco *tmp_ptr = nullptr; unique_ptr ptr; try { ptr = make_unique(real_pass, ver.get_edition(), crypto, ver.get_salt(), ver.get_iteration_count(), ver.get_kdf_hash(), ver.get_crypted_key() == nullptr); } catch(bad_alloc &) { throw Ememory("macro_tools_open_archive"); } if(!second_terminateur_offset.is_zero() || tmp_ctxt->is_an_old_start_end_archive()) // we have openned the archive by the end { if(multi_threaded_crypto > 1) { #if LIBTHREADAR_AVAILABLE tmp = tmp_ptr = new (nothrow) parallel_tronconneuse(multi_threaded_crypto, crypto_size, *(stack.top()), ver.get_edition(), ptr); if(info_details) dialog->message(tools_printf(gettext("multi-threaded cyphering layer open, with %d worker thread(s)"), multi_threaded_crypto)); #else throw Ecompilation(gettext("libthreadar is required at compilation time in order to use more than one thread for cryptography")); #endif } else { tmp = tmp_ptr = new (nothrow) tronconneuse(crypto_size, *(stack.top()), ver.get_edition(), ptr); if(info_details) dialog->message(tools_printf(gettext("single-threaded cyphering layer open"))); } if(tmp_ptr != nullptr) tmp_ptr->set_initial_shift(ver.get_initial_offset()); } else // archive openned by the beginning { if(multi_threaded_crypto > 1) { #if LIBTHREADAR_AVAILABLE tmp = tmp_ptr = new (nothrow) parallel_tronconneuse(multi_threaded_crypto, crypto_size, *(stack.top()), ver.get_edition(), ptr); if(info_details) dialog->message(tools_printf(gettext("multi-threaded cyphering layer open, with %d worker thread(s)"), multi_threaded_crypto)); #else throw Ecompilation(gettext("libthreadar is required at compilation time in order to use more than one thread for cryptography")); #endif } else { tmp = tmp_ptr = new (nothrow) tronconneuse(crypto_size, *(stack.top()), ver.get_edition(), ptr); if(info_details) dialog->message(tools_printf(gettext("single-threaded cyphering layer open"))); } if(tmp_ptr != nullptr) { tmp_ptr->set_callback_trailing_clear_data(¯o_tools_get_terminator_start); tmp_ptr->set_initial_shift((stack.top())->get_position()); if(sequential_read) elastic(*tmp_ptr, elastic_forward, ver.get_edition()); // this is line creates a temporary anonymous object and destroys it just afterward // this is necessary to skip the reading of the initial elastic buffer // nothing prevents the elastic buffer from carrying what could // be considered an escape mark. } } } catch(std::bad_alloc &) { throw Ememory("macro_tools_open_archive"); } break; case crypto_algo::scrambling: if(info_details) dialog->message(gettext("Opening cyphering layer...")); #ifdef LIBDAR_NO_OPTIMIZATION tools_secu_string_show(*dialog, string("Used clear key: "), real_pass); #endif tmp = new (nothrow) scrambler(real_pass, *(stack.top())); break; default: throw Erange("macro_tools_open_archive", gettext("Unknown encryption algorithm")); } if(tmp == nullptr) { if(crypto != crypto_algo::none || !ver.get_tape_marks()) throw Ememory("open_archive"); } else { // we always ignore read ahead as encryption layer above sar/zapette/triial_sar has no slave thread below tmp->ignore_read_ahead(!libcurl_repo); stack.push(tmp); tmp = nullptr; } stack.add_label(LIBDAR_STACK_LABEL_UNCYPHERED); // *************** building the escape layer if necessary ************ // try { if(ver.get_tape_marks()) { if(info_details) dialog->message(gettext("Opening escape sequence abstraction layer...")); if(lax) { if(!sequential_read) { dialog->pause(gettext("LAX MODE: Archive is flagged as having escape sequence (which is normal in recent archive versions). However if this is not expected, shall I assume a data corruption occurred in this field and that this flag should be ignored? (If unsure, refuse)")); ver.set_tape_marks(false); } else throw Euser_abort("this exception triggers the creation of the escape layer"); // else in lax & sequential_read, escape sequences are mandatory, we do not propose to ignore them } else throw Euser_abort("this exception triggers the creation of the escape layer"); } else // no escape layer in the archive { if(!lax) { if(sequential_read) throw Erange("macro_tools_open_archive", gettext("Sequential read asked, but this archive is flagged to not have the necessary embedded escape sequences for that operation, aborting")); } else // lax mode if(sequential_read) { dialog->message(gettext("LAX MODE: the requested sequential read mode relies on escape sequence which seem to be absent from this archive. Assuming data corruption occurred. However, if no data corruption occurred and thus no escape sequence are present in this archive, do not use sequential reading mode to explore this archive else you will just get nothing usable from it")); ver.set_tape_marks(true); throw Euser_abort("this exception triggers the creation of the escape layer"); } else // normal mode if(ver.get_edition() >= 8) // most usually escape mark are present, thus we must warn dialog->pause(gettext("LAX MODE: Archive is flagged to not have escape sequence which is not the case by default since archive format 8 (release 2.4.x). If corruption occurred and an escape sequence is present, this may lead data restoration to fail, answering no at this question will let me consider that an escape sequence layer has to be added in spite of the archive flags. Do you want to continue as suggested by the archive flag, thus without escape sequence layer?")); } } catch(Euser_abort & e) { set unjump; unjump.insert(escape::seqt_catalogue); tmp = new (nothrow) escape(stack.top(), unjump); if(tmp == nullptr) throw Ememory("open_archive"); tmp->ignore_read_ahead(!libcurl_repo); stack.push(tmp); tmp = nullptr; } stack.add_label(LIBDAR_STACK_LABEL_CLEAR); // *********** building the compression layer ************* // if(info_details) { if(ver.get_compression_algo() == compression::none) dialog->message(gettext("Opening the compression abstraction layer (compression algorithm used is none)...")); else dialog->message(gettext("Opening the compression layer...")); } version_check(*dialog, ver); if(ver.get_compression_block_size().is_zero()) { tmp = macro_tools_build_streaming_compressor(ver.get_compression_algo(), *(stack.top()), 9, // not used for decompression multi_threaded_compress); if(info_details) dialog->message(tools_printf(gettext("streamed compression layer open (single threaded)"))); } else { U_I compr_bs = 0; infinint ibs = ver.get_compression_block_size(); ibs.unstack(compr_bs); if(!ibs.is_zero()) throw Erange("macro_tools_open_layers", gettext("compression block size used in the archive exceed integer capacity of the current system")); tmp = macro_tools_build_block_compressor(ver.get_compression_algo(), *(stack.top()), 9, // not used for decompression multi_threaded_compress, compr_bs); if(info_details) dialog->message(tools_printf(gettext("block compression layer open with %d worker thread(s)"), multi_threaded_compress)); } if(tmp == nullptr) throw Ememory("open_archive"); else { #ifdef LIBTHREADAR_AVAILABLE if(multi_threaded_crypto > 1) tmp->ignore_read_ahead(!libcurl_repo); #else tmp->ignore_read_ahead(!libcurl_repo); #endif stack.push(tmp, LIBDAR_STACK_LABEL_UNCOMPRESSED); tmp = nullptr; } // ************* warning info ************************ // if(info_details) dialog->message(gettext("All layers have been created successfully")); if(ver.is_ciphered()) dialog->printf(gettext("Warning, the archive %S has been encrypted. A wrong key is not possible to detect, it would cause DAR to report the archive as corrupted"), &basename); } catch(...) { if(tmp != nullptr) delete tmp; stack.clear(); throw; } } catalogue *macro_tools_lax_search_catalogue(const shared_ptr & dialog, pile & stack, const archive_version & edition, compression compr_algo, bool info_details, bool even_partial_catalogue, const label & layer1_data_name) { catalogue *ret = nullptr; thread_cancellation thr_cancel; pile_descriptor pdesc(&stack); bool ok = false; U_I check_abort_every = 10000; U_I check_abort_count = check_abort_every; infinint offset; infinint max_offset; infinint min_offset; infinint amplitude; entree_stats stats; infinint fraction = 101; if(!dialog) throw SRC_BUG; // dialog points to nothing // obtaining from the user the fraction of the archive to inspect do { string answ = dialog->get_string(gettext("LAX MODE: The catalogue (table of contents) usually takes a few percents of the archive at its end, which percentage do you want me to scan (answer by an *integer* number between 0 and 100)? "), true); try { deci num = answ; fraction = num.computer(); if(fraction > 100) dialog->printf(gettext("LAX MODE: %i is not a valid percent value"), &fraction); } catch(Edeci & e) { dialog->printf(gettext("%S is not a valid number"), & answ); } } while(fraction > 100 || fraction.is_zero()); if(info_details) dialog->printf(gettext("LAX MODE: Beginning search of the catalogue (from the end toward the beginning of the archive, on %i %% of its length), this may take a while..."), &fraction); // finding the upper limit of the search if(stack.skip_to_eof()) max_offset = stack.get_position(); else { dialog->message(gettext("LAX MODE: Cannot skip at the end of the archive! Using current position to start the catalogue search")); max_offset = stack.get_position(); } if(max_offset.is_zero()) throw Erange("macro_tools_lax_search_catalogue", gettext("LAX MODE: Failed to read the catalogue (no data to inspect)")); if(fraction.is_zero()) throw Erange("macro_tools_lax_search_catalogue", gettext("LAX MODE: Failed to read the catalogue (0 bytes of the archive length asked to look for the catalogue)")); // calculating starting offset and offset range min_offset = ((infinint)100 - fraction)*max_offset/100; amplitude = max_offset - min_offset; if(pdesc.esc != nullptr) { dialog->message(gettext("LAX MODE: Escape sequence seems present in this archive. I have thus two different methods, either I look for the escape sequence indicating the start of the catalogue or I try each position in turn in the hope it will not be data that look like a catalogue")); try { dialog->pause(gettext("LAX MODE: Trying to locate the escape sequence (safer choice) ?")); if(!pdesc.esc->skip(min_offset)) throw SRC_BUG; if(pdesc.esc->skip_to_next_mark(escape::seqt_catalogue, true)) { dialog->message(gettext("LAX MODE: Good point! I could find the escape sequence marking the beginning of the catalogue, now trying to read it...")); pdesc.stack->flush_read_above(pdesc.esc); if(pdesc.stack->get_position() != pdesc.esc->get_position()) throw SRC_BUG; offset = pdesc.stack->get_position(); min_offset = offset; // no need to read before this position, we cannot fetch the catalogue there } else { dialog->message(gettext("LAX MODE: Escape sequence could not be found, it may have been corrupted or out of the scanned portion of the archive, trying to find the catalogue the other way")); throw Euser_abort("THIS MESSAGE SHOULD NEVER APPEAR, It branches the execution to the second way of looking for the catalogue"); } } catch(Euser_abort & e) { offset = min_offset; pdesc.stack->skip(offset); } } while(!ok) { // checking whether thread cancellation has been asked if(++check_abort_count > check_abort_every) { thr_cancel.check_self_cancellation(); check_abort_count = 0; if(info_details) { infinint ratio = (offset - min_offset)*100/amplitude; dialog->message(tools_printf(gettext("LAX MODE: %i %% remaining"), & ratio)); } } // trying to read a catalogue at the "offset" position try { ret = new (nothrow) catalogue(dialog, pdesc, edition, compr_algo, even_partial_catalogue, layer1_data_name); if(ret == nullptr) throw Ememory("macro_tools_lax_search_catalogue"); stats = ret->get_stats(); dialog->printf(gettext("Could read a catalogue data structure at offset %i, it contains the following:"), &offset); stats.listing(*dialog); dialog->pause(gettext("Do you want to use it for the operation?")); ok = true; } catch(Ebug & e) { throw; } catch(Ethread_cancel & e) { throw; } catch(Efeature & e) { throw; } catch(Ehardware & e) { throw; } catch(Escript & e) { throw; } catch(Ecompilation & e) { throw; } catch(Egeneric & e) { if(offset >= max_offset) { if(info_details) dialog->message(gettext("LAX MODE: Reached the end of the area to scan, FAILED to find any catalogue")); ret = nullptr; ok = true; } else stack.skip(++offset); } } if(ret == nullptr) throw Erange("macro_tools_lax_search_catalogue", gettext("LAX MODE: Failed to read the catalogue")); else return ret; } infinint macro_tools_get_terminator_start(generic_file & f, const archive_version & reading_ver) { terminateur term; f.skip_to_eof(); term.read_catalogue(f, false, reading_ver); return term.get_catalogue_start(); } void macro_tools_create_layers(const shared_ptr & dialog, pile & layers, header_version & ver, slice_layout & slicing, const slice_layout *ref_slicing, const shared_ptr & sauv_path_t, const string & filename, const string & extension, bool allow_over, bool warn_over, bool info_details, const infinint & pause, compression algo, U_I compression_level, U_I compression_block_size, const infinint & file_size, const infinint & first_file_size, const string & execute, crypto_algo crypto, const secu_string & pass, U_32 crypto_size, const vector & gnupg_recipients, const vector & gnupg_signatories, bool empty, const string & slice_permission, bool add_marks_for_sequential_reading, const string & user_comment, hash_algo hash, const infinint & slice_min_digits, const label & internal_name, const label & data_name, const infinint & iteration_count, hash_algo kdf_hash, U_I multi_threaded_crypto, U_I multi_threaded_compress) { #if GPGME_SUPPORT U_I gnupg_key_size; #endif try { generic_file *tmp = nullptr; escape *esc = nullptr; generic_file *level1 = nullptr; bool force_permission = (slice_permission != ""); U_I permission = force_permission ? tools_octal2int(slice_permission) : 0; // 0 or anything else, this does not matter gf_mode open_mode = gf_read_write; // by default first layer is read-write except in case of hashing or encryption // read-write mode is used when skipping back due to file change, the escape layer needs to read the few // bytes before the backward position to take care of tape marks slicing.clear(); // will eventually be set by sar if sar is used layers.clear(); secu_string real_pass = pass; if(hash != hash_algo::none || crypto != crypto_algo::none) open_mode = gf_write_only; try { bool writing_to_pipe = false; // note: // the escape object if present in the stack needs read access from below it when skipping backward // is requested (bad compression ration, changed filed at the time of backup, etc.) // ********** building the level 1 generic_file layer *********** // if(empty) { if(info_details) dialog->message(gettext("Creating low layer: Writing archive into a black hole object (equivalent to /dev/null)...")); tmp = new (nothrow) null_file(open_mode); } else if(file_size.is_zero()) // one SLICE if(filename == "-") // output to stdout { if(info_details) dialog->message(gettext("Creating low layer: Writing archive into standard output object...")); writing_to_pipe = true; tmp = macro_tools_open_archive_tuyau(dialog, 1, gf_write_only, // always write only internal_name, data_name, false, execute); //archive goes to stdout } else { if(info_details) dialog->message(gettext("Creating low layer: Writing archive into a plain file object...")); tmp = new (nothrow) trivial_sar(dialog, open_mode, filename, extension, *sauv_path_t, // entrepot !! internal_name, data_name, execute, allow_over, warn_over, force_permission, permission, hash, slice_min_digits, false); } else { sar *tmp_sar = nullptr; if(info_details) dialog->message(gettext("Creating low layer: Writing archive into a sar object (Segmentation and Reassembly) for slicing...")); tmp = tmp_sar = new (nothrow) sar(dialog, open_mode, filename, extension, file_size, first_file_size, warn_over, allow_over, pause, sauv_path_t, // shared_ptr !! internal_name, data_name, force_permission, permission, hash, slice_min_digits, false, execute); if(tmp_sar != nullptr) slicing = tmp_sar->get_slicing(); } if(tmp == nullptr) throw Ememory("op_create_in_sub"); else { layers.push(tmp); level1 = tmp; // recorded to know where to drop the archive header while the encryption layer will already be added on top tmp = nullptr; } // ******** adding cache layer if writing to pipe in order to provide limited read/write mode ***** // if(writing_to_pipe) { if(info_details) dialog->message(gettext("Adding cache layer over pipe to provide limited skippability...")); cache *c_tmp = new (nothrow) cache(*(layers.top()), true); if(c_tmp == nullptr) throw Ememory("op_create_in_sub"); else { tmp = c_tmp; // to handle the object destruction in case of exception if(open_mode == gf_read_write) c_tmp->change_to_read_write(); layers.push(c_tmp, LIBDAR_STACK_LABEL_CACHE_PIPE, true); level1 = tmp; // we will use the cache as layer where to write down the archive header tmp = nullptr; } } // ***** preparing the keys for encryption ********************* // if(crypto != crypto_algo::none || !gnupg_recipients.empty()) { if(!secu_string::is_string_secured()) dialog->message(gettext("WARNING: support for secure memory was not available at compilation time, in case of heavy memory load, this may lead the password/passphrase provided to be wrote to disk (swap space) in clear. You have been warned!")); } if(!gnupg_recipients.empty()) { #if GPGME_SUPPORT memory_file *key = new (nothrow) memory_file(); if(key == nullptr) throw Ememory("macro_tools_create_layers"); try { // generating random key for symmetric encryption if(info_details) dialog->message(gettext("Generating random key for symmetric encryption...")); switch(crypto) { case crypto_algo::none: throw SRC_BUG; case crypto_algo::scrambling: throw Erange("macro_tools_create_layers", gettext("Scrambling is a very weak encryption algorithm, this is a non-sens to use with asymmetric encryption")); break; case crypto_algo::blowfish: case crypto_algo::aes256: case crypto_algo::twofish256: case crypto_algo::serpent256: case crypto_algo::camellia256: gnupg_key_size = tools_max(crypto_sym::max_key_len(crypto), crypto_sym::max_key_len_libdar(crypto)); break; default: throw SRC_BUG; } if(gnupg_key_size == 0) throw SRC_BUG; secu_memory_file clear(gnupg_key_size); clear.randomize(gnupg_key_size); U_I iter = 0; U_I next = 1; // checking the strength of the random key switch(crypto) { case crypto_algo::none: throw SRC_BUG; case crypto_algo::scrambling: break; case crypto_algo::blowfish: case crypto_algo::aes256: case crypto_algo::twofish256: case crypto_algo::serpent256: case crypto_algo::camellia256: while(!crypto_sym::is_a_strong_password(crypto, clear.get_contents())) { clear.randomize(gnupg_key_size); ++iter; if(iter % next == 0) dialog->message(tools_printf(gettext("For your information, this is the iteration %d for which the randomly generated key is reported to be weak by libgcrypt, continuing generating another random key... patience"), iter)); next *= 10; } if(iter > 1) dialog->message(tools_printf(gettext("... A strong randomly generated key could be found after %d iteration(s)"), iter)); break; default: throw SRC_BUG; } if(info_details) dialog->message(gettext("Key generated")); #ifdef LIBDAR_NO_OPTIMIZATION tools_secu_string_show(*dialog, string("Generated key: "), clear.get_contents()); #endif // encrypting the symmetric key with asymetric algorithm crypto_asym engine(dialog); if(!gnupg_signatories.empty()) { if(gnupg_recipients.size() > 1) dialog->message(gettext("WARNING: When signing an archive with more than one recipient, there is a theorical risk that a recipient forges a slightly modified version of the generated archive without breaking the signature validation. It is advised in that context to sign the archive externally by calling \"gpg --detach-sign\" once dar has completed the archive")); engine.set_signatories(gnupg_signatories); } clear.skip(0); key->skip(0); if(clear.get_size().is_zero()) throw SRC_BUG; engine.encrypt(gnupg_recipients, clear, *key); // using the generated key for the crypto layer that will be setup below real_pass = clear.get_contents(); if(real_pass.get_size() == 0) throw SRC_BUG; if(crypto == crypto_algo::none) crypto = crypto_algo::blowfish; // updating the hader version with the symmetric key encrypted with the asymmetrical algorithm ver.set_crypted_key(key); key = nullptr; // now the pointed to object is under the responsibility of ver object } catch(...) { if(key != nullptr) delete key; throw; } #else throw Ecompilation("Strong Encryption support"); #endif } if(crypto != crypto_algo::none) { // optaining a password on-fly if necessary if(real_pass.get_size() == 0) { secu_string t1 = dialog->get_secu_string(tools_printf(gettext("Archive %S requires a password: "), &filename), false); secu_string t2 = dialog->get_secu_string(gettext("Please confirm your password: "), false); if(t1 == t2) real_pass = t1; else throw Erange("op_create_in_sub", gettext("The two passwords are not identical. Aborting")); } } // ************ building the encryption layer if required ****** // // the encryption layer need to be setup now, for the salt to be determined, // it will be recorded in the archive header which has to be wrote down at the // beginning of the archive. // Once the archvie header has been wrote the initial_shift (offset of the first // encrypted data) will be possible to be given to the encryption layer // // this info is necessary to determine the encryption block boundaries. proto_tronco *tmp_tronco = nullptr; bool use_pkcs5 = gnupg_recipients.empty(); switch(crypto) { case crypto_algo::scrambling: if(info_details) dialog->message(gettext("Adding a new layer on top: scrambler object...")); tmp = new (nothrow) scrambler(real_pass, *(layers.top())); #ifdef LIBDAR_NO_OPTIMIZATION tools_secu_string_show(*dialog, string("real_pass used: "), real_pass); #endif break; case crypto_algo::blowfish: case crypto_algo::aes256: case crypto_algo::twofish256: case crypto_algo::serpent256: case crypto_algo::camellia256: if(info_details) dialog->message(gettext("Adding a new layer on top: Strong encryption object...")); try { unique_ptr ptr = make_unique(real_pass, macro_tools_supported_version, crypto, "", // the salt, will be generated by the crypto_module iteration_count, kdf_hash, use_pkcs5); if(!ptr) throw Ememory("macro_tools_create_layers"); // updating the archive_header with the KDF parameters crypto_sym* sym_ptr = dynamic_cast(ptr.get()); if(sym_ptr != nullptr) { if(use_pkcs5) { ver.set_salt(sym_ptr->get_salt()); ver.set_iteration_count(iteration_count); ver.set_kdf_hash(kdf_hash); } } else throw SRC_BUG; // today only crypto_sym is used, the // dynamic_cast should thus always succeed if(multi_threaded_crypto > 1) { #if LIBTHREADAR_AVAILABLE tmp = tmp_tronco = new (nothrow) parallel_tronconneuse(multi_threaded_crypto, crypto_size, *(layers.top()), macro_tools_supported_version, ptr); if(info_details) dialog->message(tools_printf(gettext("multi-threaded cyphering layer open, with %d worker thread(s)"), multi_threaded_crypto)); #else throw Ecompilation(gettext("libthreadar is required at compilation time in order to use more than one thread for cryptography")); #endif } else { tmp = tmp_tronco = new (nothrow) tronconneuse(crypto_size, *(layers.top()), macro_tools_supported_version, ptr); if(info_details) dialog->message(tools_printf(gettext("single-threaded cyphering layer open"))); } } catch(std::bad_alloc &) { throw Ememory("macro_tools_create_layers"); } #ifdef LIBDAR_NO_OPTIMIZATION tools_secu_string_show(*dialog, string("real_pass used: "), real_pass); #endif break; case crypto_algo::none: if(!writing_to_pipe) { if(info_details) dialog->message(gettext("Adding a new layer on top: Caching layer for better performances...")); tmp = new (nothrow) cache(*(layers.top()), false); if(tmp != nullptr) level1 = tmp; // new level where to write down the archive header, this is not an encryption layer } else tmp = nullptr; // a cache is already present just below break; default: throw SRC_BUG; // cryto value should have been checked before } // ****** we can now add the crypto layer to the stack ****** // if(!writing_to_pipe || crypto != crypto_algo::none) { if(tmp == nullptr) throw Ememory("op_create_in_sub"); else { layers.push(tmp); tmp = nullptr; } } else { if(tmp != nullptr) throw SRC_BUG; } // ******** finishing the creation of the archive header and writing it down (in clear!) at the beginning of the archive *** // ver.set_edition(macro_tools_supported_version); ver.set_compression_algo(algo); if(algo == compression::lzo1x_1_15 || algo == compression::lzo1x_1) ver.set_compression_algo(compression::lzo); ver.set_command_line(user_comment); ver.set_sym_crypto_algo(crypto); ver.set_tape_marks(add_marks_for_sequential_reading); ver.set_signed(!gnupg_signatories.empty()); ver.set_compression_block_size(compression_block_size); if(ref_slicing != nullptr) { slice_layout *tmp = new (nothrow) slice_layout(*ref_slicing); if(tmp == nullptr) throw Ememory("macro_tools_create_layers"); try { ver.set_slice_layout(tmp); tmp = nullptr; // now tmp is managed by ver } catch(...) { if(tmp != nullptr) delete tmp; throw; } } else ver.clear_slice_layout(); // we drop the header at the beginning of the archive in any case (to be able to // know whether sequential reading is possible or not, and if sequential reading // is asked, be able to get the required parameter to read the archive. // It also serves as a backup copy for normal reading if the end of the archive // is corrupted. if(info_details) dialog->message(gettext("Writing down the archive header...")); if(level1 != nullptr) ver.write(*level1); // we write to the level1 which is just below the encryption layer, thus the header is wrote unciphered else throw SRC_BUG; // now we can add the initial offset in the archive_header datastructure, which will be written // a second time, but at the end of the archive. If we start reading the archive from the end // we must know where ended the initial archive header. ver.set_initial_offset(level1->get_position()); // ********** now that the archive header has been wrote down, we can set the initial_shift for the encryption layer ***** // if(tmp_tronco != nullptr) tmp_tronco->set_initial_shift(ver.get_initial_offset()); // ***** adding a elastic buffer as first ciphered data after the clear archive header ****** // if(crypto != crypto_algo::none) { if(info_details) dialog->message(gettext("Writing down the initial elastic buffer through the encryption layer...")); macro_tools_add_elastic_buffer(layers, GLOBAL_ELASTIC_BUFFER_SIZE, 0, 0); } // ********** if required building the escape layer ***** // if(add_marks_for_sequential_reading) { set unjump; if(info_details) dialog->message(gettext("Adding a new layer on top: Escape layer to allow sequential reading...")); unjump.insert(escape::seqt_catalogue); tmp = esc = new (nothrow) escape(layers.top(), unjump); if(tmp == nullptr) throw Ememory("op_create_in_sub"); else { layers.push(tmp); tmp = nullptr; } } // ********** building the level2 layer (compression) ************************ // if(info_details && algo != compression::none) dialog->message(gettext("Adding a new layer on top: compression...")); if(compression_block_size == 0) { tmp = macro_tools_build_streaming_compressor(algo, *(layers.top()), compression_level, multi_threaded_compress); if(info_details) dialog->message(tools_printf(gettext("Adding a streamed compression layer"))); } else { tmp = macro_tools_build_block_compressor(algo, *(layers.top()), compression_level, multi_threaded_compress, compression_block_size); if(info_details) dialog->message(tools_printf(gettext("Adding block compression layer, with %d worker thread(s)"), multi_threaded_compress)); } if(tmp == nullptr) throw Ememory("op_create_in_sub"); else { layers.push(tmp); tmp = nullptr; } if(info_details) dialog->message(gettext("All layers have been created successfully")); } catch(...) { if(tmp != nullptr) { delete tmp; tmp = nullptr; } throw; } } catch(Erange &e) { string msg = string(gettext("Error creating archive layers: ")) + e.get_message(); throw Edata(msg); } } void macro_tools_close_layers(const shared_ptr & dialog, pile & layers, const header_version & ver, const catalogue & cat, bool info_details, crypto_algo crypto, compression algo, const vector & gnupg_recipients, const vector & gnupg_signatories, bool empty) { terminateur coord; pile_descriptor pdesc(&layers); proto_tronco *tronco_ptr = nullptr; scrambler *scram_ptr = nullptr; memory_file *hash_to_sign = nullptr; tlv *signed_hash = nullptr; hash_fichier *hasher = nullptr; // ***************** sanity check ************************************** // if(!gnupg_signatories.empty() && gnupg_recipients.empty()) throw SRC_BUG; // cannot sign without encryption in the current implementation pdesc.check(false); // *********** flushing pending writes and reseting compressor ******** // if(pdesc.esc != nullptr) { layers.sync_write_above(pdesc.esc); // esc is now up to date pdesc.esc->add_mark_at_current_position(escape::seqt_catalogue); } else // still need to reset the compressor // in order to be able to read compressed data starting at that position pdesc.compr->sync_write(); // ********* if archive is signed adding a hash layer to get a hash of *// // *** the catalogue. This hash will then be signed and encrypted and *// // ** dropped as is just after the end of the catalogue */ try { if(!gnupg_signatories.empty()) { generic_to_global_file *global_hash_to_sign = nullptr; generic_to_global_file *global_layers = nullptr; hash_to_sign = new (nothrow) memory_file(); if(hash_to_sign == nullptr) throw Ememory("macro_tools_close_layers"); signed_hash = new (nothrow) tlv(); if(signed_hash == nullptr) throw Ememory("macro_tools_close_layers"); try { global_hash_to_sign = new (nothrow) generic_to_global_file(dialog, hash_to_sign, gf_write_only); global_layers = new (nothrow) generic_to_global_file(dialog, layers.top(), gf_write_only); if(global_hash_to_sign == nullptr || global_layers == nullptr) throw Ememory("macro_tools_close_layers"); hasher = new (nothrow) hash_fichier(dialog, global_layers, string("x"), global_hash_to_sign, hash_algo::sha512); if(hasher == nullptr) throw Ememory("macro_tools_close_layers"); // at this stage, hasher has been built and now manages // both global_hash_to_sign and global_layers which will // be destroyed with hasher, when it will no more be needed } catch(...) { if(global_hash_to_sign != nullptr) delete global_hash_to_sign; if(global_layers != nullptr) delete global_layers; throw; } layers.push(hasher); pdesc = pile_descriptor(&layers); } // *********** writing down the catalogue of the archive *************** // coord.set_catalogue_start(layers.get_position()); if(info_details) dialog->message(gettext("Writing down archive contents...")); cat.reset_dump(); cat.dump(pdesc); // removing the hash from the top of the stack now that the catalogue has been dropped // if(hasher != nullptr) { hasher->terminate(); // this drops the hash to hash_to_sign if(layers.top() != hasher) throw SRC_BUG; if(layers.pop() != hasher) throw SRC_BUG; pdesc = pile_descriptor(&layers); } // if signing is activated ... // if(!gnupg_signatories.empty()) { crypto_asym engine(dialog); if(info_details) dialog->message(gettext("Calculating the signature of the catalogue hash...")); // ciphering and signing the hash of the catalogue // if(hash_to_sign == nullptr) throw SRC_BUG; else hash_to_sign->skip(0); if(signed_hash == nullptr) throw SRC_BUG; engine.set_signatories(gnupg_signatories); engine.encrypt(gnupg_recipients, *hash_to_sign, *signed_hash); // writing down the size of the hash followed by the hash // if(info_details) dialog->message(gettext("Writing down the signed hash of the catalogue...")); signed_hash->dump(layers); } } catch(...) { if(hasher != nullptr) { if(layers.top() == hasher) { if(layers.pop() != hasher) throw SRC_BUG; pdesc = pile_descriptor(&layers); } delete hasher; hasher = nullptr; } if(signed_hash != nullptr) { delete signed_hash; signed_hash = nullptr; } if(hash_to_sign != nullptr) { delete hash_to_sign; hash_to_sign = nullptr; } throw; } if(hasher != nullptr) { delete hasher; hasher = nullptr; } if(signed_hash != nullptr) { delete signed_hash; signed_hash = nullptr; } if(hash_to_sign != nullptr) { delete hash_to_sign; hash_to_sign = nullptr; } // releasing the compression layer if(info_details && algo != compression::none) dialog->message(gettext("Closing the compression layer...")); if(!layers.pop_and_close_if_type_is(pdesc.compr)) throw SRC_BUG; else pdesc.compr = nullptr; // releasing the escape layer if(pdesc.esc != nullptr) { if(info_details) dialog->message(gettext("Closing the escape layer...")); if(!layers.pop_and_close_if_type_is(pdesc.esc)) throw SRC_BUG; pdesc.esc = nullptr; } // *********** writing down the first terminator at the end of the archive *************** // tronco_ptr = dynamic_cast(layers.top()); scram_ptr = dynamic_cast(layers.top()); if(info_details) dialog->message(gettext("Writing down the first archive terminator...")); coord.dump(layers); // since format "04" the terminateur is encrypted if(crypto != crypto_algo::none) { if(info_details) dialog->message(gettext("writing down the final elastic buffer through the encryption layer...")); // obtaining the crypto block size (for clear data) U_32 block_size = 0; if(tronco_ptr != nullptr) block_size = tronco_ptr->get_clear_block_size(); if(tronco_ptr == nullptr && scram_ptr == nullptr) throw SRC_BUG; // calculating the current offset modulo block_size U_32 offset = 0; if(block_size > 0) { infinint times = 0; infinint reste = 0; euclide(layers.get_position(), infinint(block_size), times, reste); reste.unstack(offset); if(!reste.is_zero()) throw SRC_BUG; } macro_tools_add_elastic_buffer(layers, GLOBAL_ELASTIC_BUFFER_SIZE, block_size, offset); // terminal elastic buffer (after terminateur to protect against // plain text attack on the terminator string) } // releasing memory by calling destructors and releases file descriptors if(tronco_ptr != nullptr || scram_ptr != nullptr) { if(info_details) dialog->message(gettext("Closing the encryption layer...")); } if(tronco_ptr != nullptr) tronco_ptr->write_end_of_file(); if(tronco_ptr != nullptr) { if(!layers.pop_and_close_if_type_is(tronco_ptr)) throw SRC_BUG; } if(scram_ptr != nullptr) { if(!layers.pop_and_close_if_type_is(scram_ptr)) throw SRC_BUG; } // *********** writing down the trailier_version with the second terminateur *************** // if(info_details) dialog->message(gettext("Writing down archive trailer...")); coord.set_catalogue_start(layers.get_position()); ver.write(layers); if(info_details) dialog->message(gettext("Writing down the second archive terminator...")); // due to the possibility a file get shorter after being resaved because it was modified at the // time it was read for backup, we must be sure the last terminator is well positionned at end of file layers.skip_to_eof(); coord.dump(layers); layers.sync_write(); // *********** closing the archive ******************** // if(info_details) dialog->message(gettext("Closing archive low layer...")); // closing all generic_files remaining in the layers try { layers.terminate(); // this way we can propagate exceptions // and clear() the layers at the same time } catch(...) { layers.clear(); throw; } layers.clear(); if(info_details) dialog->message(gettext("Archive is closed.")); } range macro_tools_get_slices(const cat_nomme *obj, slice_layout sl) { range slices; infinint offset; // used temporarily to record data, EA and FSA offsets infinint slice_num, slice_offset; infinint low; const cat_inode *tmp_inode = dynamic_cast(obj); const cat_file *tmp_file = dynamic_cast(obj); const cat_mirage *tmp_mir = dynamic_cast(obj); if(obj == nullptr) throw SRC_BUG; if(tmp_mir != nullptr) { tmp_inode = tmp_mir->get_inode(); tmp_file = dynamic_cast(tmp_inode); } if(tmp_inode != nullptr) { if(!sl.first_size.is_zero()) { if(tmp_inode->ea_get_saved_status() == ea_saved_status::full) { if(tmp_inode->ea_get_offset(offset)) { sl.which_slice(offset, slice_num, slice_offset); low = slice_num; offset += tmp_inode->ea_get_size(); sl.which_slice(offset, slice_num, slice_offset); slices += range(low, slice_num); } else throw SRC_BUG; // has EA saved but no offset ?!? } if(tmp_inode->fsa_get_saved_status() == fsa_saved_status::full) { if(tmp_inode->fsa_get_offset(offset)) { sl.which_slice(offset, slice_num, slice_offset); low = slice_num; offset += tmp_inode->fsa_get_size(); sl.which_slice(offset, slice_num, slice_offset); slices += range(low, slice_num); } else throw SRC_BUG; } } } if(tmp_file != nullptr) { if(tmp_file->get_saved_status() == saved_status::saved) { offset = tmp_file->get_offset(); sl.which_slice(offset, slice_num, slice_offset); low = slice_num; offset += tmp_file->get_storage_size(); sl.which_slice(offset, slice_num, slice_offset); slices += range(low, slice_num); } } return slices; } trivial_sar *macro_tools_open_archive_tuyau(const shared_ptr & dialog, S_I fd, gf_mode mode, const label & internal_name, const label & data_name, bool slice_header_format_07, const std::string & execute) { generic_file *tmp = nullptr; trivial_sar *ret = nullptr; try { tmp = new (nothrow) tuyau(dialog, fd, mode); if(tmp == nullptr) throw Ememory("macro_tools_open_archive_tuyau"); ret = new (nothrow) trivial_sar(dialog, tmp, internal_name, data_name, slice_header_format_07, execute); if(ret == nullptr) throw Ememory("macro_tools_open_archive_tuyau"); else tmp = nullptr; } catch(...) { if(ret != nullptr) delete ret; if(tmp != nullptr) delete tmp; throw; } return ret; } void macro_tools_open_pipes(const shared_ptr & dialog, const string &input, const string & output, tuyau *&in, tuyau *&out) { in = out = nullptr; try { if(input != "") in = new (nothrow) tuyau(dialog, input, gf_read_only); else in = new (nothrow) tuyau(dialog, 0, gf_read_only); // stdin by default if(in == nullptr) throw Ememory("tools_open_pipes"); if(output != "") out = new (nothrow) tuyau(dialog, output, gf_write_only); else out = new (nothrow) tuyau(dialog, 1, gf_write_only); // stdout by default if(out == nullptr) throw Ememory("tools_open_pipes"); } catch(...) { if(in != nullptr) delete in; if(out != nullptr) delete out; throw; } } proto_compressor* macro_tools_build_streaming_compressor(compression algo, generic_file & base, U_I compression_level, U_I num_workers) { proto_compressor* ret; switch(algo) { case compression::none: case compression::gzip: case compression::bzip2: case compression::xz: ret = new (nothrow) compressor(algo, base, compression_level); break; case compression::lzo: case compression::lzo1x_1_15: case compression::lzo1x_1: case compression::lz4: if(num_workers > 1) { #if LIBTHREADAR_AVAILABLE ret = new (nothrow) parallel_block_compressor(num_workers, make_compress_module_ptr(algo, compression_level), base, PRE_2_7_0_LZO_BLOCK_SIZE); #else throw Ecompilation(gettext("libthreadar is required at compilation time in order to use more than one thread for block compression")); #endif } else ret = new (nothrow) block_compressor(make_compress_module_ptr(algo, compression_level), base, PRE_2_7_0_LZO_BLOCK_SIZE); break; case compression::zstd: ret = new (nothrow) compressor_zstd(base, compression_level); break; default: throw SRC_BUG; } if(ret == nullptr) throw Ememory("macro_tools_build_streaming_compressor"); return ret; } proto_compressor* macro_tools_build_block_compressor(compression algo, generic_file & base, U_I compression_level, U_I num_workers, U_I block_size) { proto_compressor* ret = nullptr; if(num_workers > 1) { #if LIBTHREADAR_AVAILABLE ret = new (nothrow) parallel_block_compressor(num_workers, make_compress_module_ptr(algo, compression_level), base, block_size); #else throw Ecompilation(gettext("libthreadar is required at compilation time in order to use more than one thread for block compression")); #endif } else { ret = new (nothrow) block_compressor(make_compress_module_ptr(algo, compression_level), // compression level is not used here base, block_size); } if(ret == nullptr) throw Ememory("macro_tools_build_block_compressor"); return ret; } //////////// STATIC FUNCTIONS ////////////////////////////////////////// static void version_check(user_interaction & dialog, const header_version & ver) { if(ver.get_edition() > macro_tools_supported_version) dialog.pause(gettext("The format version of the archive is too high for that software version, try reading anyway?")); } static void macro_tools_add_elastic_buffer(generic_file & f, U_32 max_size, U_32 modulo, U_32 offset) { U_32 size = tools_pseudo_random(max_size-1) + 1; // range from 1 to max_size; if(modulo > 0) { U_32 shift = modulo - (offset % modulo); size = (size/modulo)*modulo + shift; } elastic tic = size; char *buffer = new (nothrow) char[tic.get_size()]; if(buffer == nullptr) throw Ememory("tools_add_elastic_buffer"); try { tic.dump((unsigned char *)buffer, tic.get_size()); f.write(buffer, tic.get_size()); } catch(...) { delete [] buffer; throw; } delete [] buffer; } static unique_ptr make_compress_module_ptr(compression algo, U_I compression_level) { unique_ptr ret; try { switch(algo) { case compression::none: throw SRC_BUG; case compression::gzip: ret = make_unique(compression_level); break; case compression::bzip2: ret = make_unique(compression_level); break; case compression::lzo: case compression::lzo1x_1_15: case compression::lzo1x_1: ret = make_unique(algo, compression_level); break; case compression::xz: ret = make_unique(compression_level); break; case compression::zstd: ret = make_unique(compression_level); break; case compression::lz4: ret = make_unique(compression_level); break; default: throw SRC_BUG; } } catch(bad_alloc &) { throw Ememory("make_compress_module_ptr"); } if(!ret) throw SRC_BUG; // we would return a null pointer! return ret; } } // end of namespace dar-2.7.15/src/libdar/cat_chardev.hpp0000644000175000017500000000560314636066467014323 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_chardev.hpp /// \brief class used to record character special devices in a catalogue /// \ingroup Private #ifndef CAT_CHARDEV_HPP #define CAT_CHARDEV_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_device.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the char device class class cat_chardev : public cat_device { public: cat_chardev(const infinint & uid, const infinint & gid, U_16 perm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & name, U_16 major, U_16 minor, const infinint & fs_device) : cat_device(uid, gid, perm, last_access, last_modif, last_change, name, major, minor, fs_device) {}; cat_chardev(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small) : cat_device(dialog, pdesc, reading_ver, saved, small) {}; cat_chardev(const cat_chardev & ref) = default; cat_chardev(cat_chardev && ref) noexcept = default; cat_chardev & operator = (const cat_chardev & ref) = default; cat_chardev & operator = (cat_chardev && ref) = default; ~cat_chardev() = default; virtual bool operator == (const cat_entree & ref) const override; // using dump from cat_device class // using method is_more_recent_than() from cat_device class // using method has_changed_since() from cat_device class virtual unsigned char signature() const override { return 'c'; }; virtual std::string get_description() const override { return "char device"; }; virtual cat_entree *clone() const override { return new (std::nothrow) cat_chardev(*this); }; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/user_interaction.hpp0000644000175000017500000001117114636066467015432 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file user_interaction.hpp /// \brief defines the interaction interface between libdar and users. /// \ingroup API #ifndef USER_INTERACTION_HPP #define USER_INTERACTION_HPP #include "../my_config.h" #include #include "secu_string.hpp" #include "infinint.hpp" namespace libdar { /// \addtogroup API /// @{ /// This is a pure virtual class that is used by libdar when interaction with the user is required. /// You can base your own class on it using C++ inheritance /// or use the predifined inherited classes user_interaction_callback which implements /// the interaction based on callback functions. class user_interaction { public: user_interaction() {}; user_interaction(const user_interaction & ref) = default; user_interaction(user_interaction && ref) noexcept = default; user_interaction & operator = (const user_interaction & ref) = default; user_interaction & operator = (user_interaction && ref) noexcept = default; virtual ~user_interaction() = default; // the following methode are used by libdar and rely in their inherited_* versions // than must be defined in the inherited classes void message(const std::string & message); void pause(const std::string & message); std::string get_string(const std::string & message, bool echo); secu_string get_secu_string(const std::string & message, bool echo); /// libdar uses this call to format output before send to message() method. /// This is not a virtual method, it has not to be overwritten, it is /// just a sublayer over warning() /// Supported masks for the format string are: /// - \%s \%c \%d \%\% (normal behavior) /// - \%i (matches infinint *) /// - \%S (matches std::string *) /// . virtual void printf(const char *format, ...); protected: /// method used to display a warning or a message to the user. /// /// \param[in] message is the message to display. /// \note messages passed by libdar are not ending with a newline by default /// its up to the implementation to separate messages by the adequate mean virtual void inherited_message(const std::string & message) = 0; /// method used to ask a boolean question to the user. /// \param[in] message The boolean question to ask to the user /// \return the answer of the user (true/yes or no/false) /// \note messages passed by libdar are not ending with a newline by default /// its up to the implementation to separate messages by the adequate mean virtual bool inherited_pause(const std::string & message) = 0; /// method used to ask a question that needs an arbitrary answer. /// \param[in] message is the question to display to the user. /// \param[in] echo is set to false is the answer must not be shown while the user answers. /// \return the user's answer. /// \note messages passed by libdar are not ending with a newline by default /// its up to the implementation to separate messages by the adequate mean virtual std::string inherited_get_string(const std::string & message, bool echo) = 0; /// same a get_string() but uses libdar::secu_string instead of std::string /// \param[in] message is the question to display to the user. /// \param[in] echo is set to false is the answer must not be shown while the user answers. /// \return the user's answer. /// \note messages passed by libdar are not ending with a newline by default /// its up to the implementation to separate messages by the adequate mean virtual secu_string inherited_get_secu_string(const std::string & message, bool echo) = 0; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mycurl_easyhandle_sharing.hpp0000644000175000017500000000567314636066467017312 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mycurl_easyhandle_sharing.hpp /// \brief used to optimize network session establised by libcurl /// \ingroup Private #ifndef MYCURL_EASYHANDLE_SHARING_HPP #define MYCURL_EASYHANDLE_SHARING_HPP #include "../my_config.h" extern "C" { #if LIBCURL_AVAILABLE #if HAVE_CURL_CURL_H #include #endif #endif } // end extern "C" #include #include "erreurs.hpp" #include "mycurl_easyhandle_node.hpp" namespace libdar { /// \addtogroup Private /// @{ #if LIBCURL_AVAILABLE /// manage libcurl easy_handle and its duplications class mycurl_easyhandle_sharing { public: mycurl_easyhandle_sharing() {}; // fields "table" and "global_params" are objects are get initialized by their default constructors mycurl_easyhandle_sharing(const mycurl_easyhandle_sharing & ref): global_params(ref.global_params) { table.clear(); }; mycurl_easyhandle_sharing(mycurl_easyhandle_sharing && ref) noexcept: table(std::move(ref.table)), global_params(std::move(ref.global_params)) {}; mycurl_easyhandle_sharing & operator = (const mycurl_easyhandle_sharing & ref) = delete; mycurl_easyhandle_sharing & operator = (mycurl_easyhandle_sharing && ref) noexcept = delete; ~mycurl_easyhandle_sharing() = default; /// global options are applied to all mycurl_easyhandle_node provided by this object template void setopt_global(CURLoption opt, const T & val) { global_params.add(opt, val); } /// obtain a libcurl handle wrapped in the adhoc classes to allow copy and cloning /// \note once no more needed reset() the shared_ptr to have this object recycled std::shared_ptr alloc_instance(); private: std::deque > table; mycurl_param_list global_params; }; #else class mycurl_easyhandle_sharing { public: mycurl_easyhandle_sharing() { throw Ecompilation("remote repository"); }; }; #endif /// @} } // end of namespace #endif dar-2.7.15/src/libdar/user_interaction_callback5.cpp0000644000175000017500000002272114636067146017324 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_FCNTL_H #include #endif #if STDC_HEADERS #include #endif } // end extern "C" #include #include "user_interaction_callback5.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "integers.hpp" #include "deci.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar5 { user_interaction_callback::user_interaction_callback(void (*x_warning_callback)(const string &x, void *context), bool (*x_answer_callback)(const string &x, void *context), string (*x_string_callback)(const string &x, bool echo, void *context), secu_string (*x_secu_string_callback)(const string &x, bool echo, void *context), void *context_value) { NLS_SWAP_IN; try { if(x_warning_callback == nullptr || x_answer_callback == nullptr) throw Elibcall("user_interaction_callback::user_interaction_callback", libdar::dar_gettext("nullptr given as argument of user_interaction_callback()")); warning_callback = x_warning_callback; answer_callback = x_answer_callback; string_callback = x_string_callback; secu_string_callback = x_secu_string_callback; tar_listing_callback = nullptr; dar_manager_show_files_callback = nullptr; dar_manager_contents_callback = nullptr; dar_manager_statistics_callback = nullptr; dar_manager_show_version_callback = nullptr; context_val = context_value; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void user_interaction_callback::pause(const string & message) { if(answer_callback == nullptr) throw SRC_BUG; else { try { if(! (*answer_callback)(message, context_val)) throw Euser_abort(message); } catch(Euser_abort & e) { throw; } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::pause", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::pause", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } void user_interaction_callback::inherited_warning(const string & message) { if(warning_callback == nullptr) throw SRC_BUG; else { try { (*warning_callback)(message + '\n', context_val); } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::warning", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::warning", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } string user_interaction_callback::get_string(const string & message, bool echo) { if(string_callback == nullptr) throw SRC_BUG; else { try { return (*string_callback)(message, echo, context_val); } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::get_string", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::get_string", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } secu_string user_interaction_callback::get_secu_string(const string & message, bool echo) { if(string_callback == nullptr) throw SRC_BUG; else { try { return (*secu_string_callback)(message, echo, context_val); } catch(Ebug & e) { throw; } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::get_secu_string", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::get_secu_string", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } void user_interaction_callback::listing(const string & flag, const string & perm, const string & uid, const string & gid, const string & size, const string & date, const string & filename, bool is_dir, bool has_children) { if(tar_listing_callback != nullptr) { try { (*tar_listing_callback)(flag, perm, uid, gid, size, date, filename, is_dir, has_children, context_val); } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::listing", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::listing", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } void user_interaction_callback::dar_manager_show_files(const string & filename, bool available_data, bool available_ea) { if(dar_manager_show_files_callback != nullptr) { try { (*dar_manager_show_files_callback)(filename, available_data, available_ea, context_val); } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::dar_manager_show_files", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::dar_manager_show_files", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } void user_interaction_callback::dar_manager_contents(U_I number, const std::string & chemin, const std::string & archive_name) { if(dar_manager_contents_callback != nullptr) { try { (*dar_manager_contents_callback)(number, chemin, archive_name, context_val); } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::dar_manager_contents", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::dar_manager_contents", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } void user_interaction_callback::dar_manager_statistics(U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea) { if(dar_manager_statistics_callback != nullptr) { try { (*dar_manager_statistics_callback)(number, data_count, total_data, ea_count, total_ea, context_val); } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::dar_manager_statistics", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::dar_manager_statistics", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } void user_interaction_callback::dar_manager_show_version(U_I number, const string & data_date, const string & data_presence, const string & ea_date, const string & ea_presence) { if(dar_manager_show_version_callback != nullptr) { try { (*dar_manager_show_version_callback)(number, data_date, data_presence, ea_date, ea_presence, context_val); } catch(Egeneric & e) { throw Elibcall("user_interaction_callback::dar_manager_show_version", string(libdar::dar_gettext("No exception allowed from libdar callbacks")) + ": " + e.get_message()); } catch(...) { throw Elibcall("user_interaction_callback::dar_manager_show_version", libdar::dar_gettext("No exception allowed from libdar callbacks")); } } } user_interaction * user_interaction_callback::clone() const { user_interaction *ret = new (nothrow) user_interaction_callback(*this); // copy constructor if(ret == nullptr) throw Ememory("user_interaction_callback::clone"); else return ret; } } // end of namespace dar-2.7.15/src/libdar/secu_memory_file.cpp0000644000175000017500000000472614636066467015406 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "secu_memory_file.hpp" using namespace std; namespace libdar { bool secu_memory_file::skip(const infinint & pos) { infinint tmp = pos; if(is_terminated()) throw SRC_BUG; if(tmp >= data.get_size()) { position = data.get_size(); return false; } else { position = 0; tmp.unstack(position); if(!tmp.is_zero()) throw SRC_BUG; // pos < data.size(), which is typed U_I as well as position return true; } } bool secu_memory_file::skip_to_eof() { if(is_terminated()) throw SRC_BUG; position = data.get_size(); return true; } bool secu_memory_file::skip_relative(S_I x) { bool ret = false; if(is_terminated()) throw SRC_BUG; if(x < 0) { U_I tx = -x; if(position < tx) { position = 0; ret = false; } else { position -= tx; ret = true; } } else { position += x; if(position > data.get_size()) { position = data.get_size(); ret = false; } else ret = true; } return ret; } U_I secu_memory_file::inherited_read(char *a, U_I size) { U_I lu = 0; const char *deb = data.c_str() + position; while(lu < size && position + lu < data.get_size()) { *a = *deb; ++lu; ++a; ++deb; } position += lu; return lu; } void secu_memory_file::inherited_write(const char *a, U_I size) { data.append_at(position, a, size); position += size; } } // end of namespace dar-2.7.15/src/libdar/sar_tools.hpp0000644000175000017500000000455514636066467014072 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file sar_tools.hpp /// \brief a set of tools aims to help Segmentation And Reassemblement (sar) class /// \ingroup Private #ifndef SAR_TOOLS_HPP #define SAR_TOOLS_HPP #include "../my_config.h" #include #include "infinint.hpp" #include "entrepot.hpp" namespace libdar { /// \addtogroup Private /// @{ extern std::string sar_tools_make_filename(const std::string & base_name, const infinint & num, const infinint & min_digits, const std::string & ext); extern bool sar_tools_extract_num(const std::string & filename, const std::string & base_name, const infinint & min_digits, const std::string & ext, infinint & ret); extern bool sar_tools_get_higher_number_in_dir(user_interaction & ui, entrepot & entr, const std::string & base_name, const infinint & min_digits, const std::string & ext, infinint & ret); extern void sar_tools_remove_higher_slices_than(entrepot & entr, const std::string & base_name, const infinint & min_digits, const std::string & ext, const infinint & higher_slice_num_to_keep, user_interaction & ui); extern std::string sar_tools_make_padded_number(const std::string & num, const infinint & min_digits); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/null_file.hpp0000644000175000017500000000732014636066467014027 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file null_file.hpp /// \brief /dev/null type file implementation under the generic_file interface /// \ingroup Private /// /// this class is used in particular when doing dry-run execution #ifndef NULL_FILE_HPP #define NULL_FILE_HPP #include "../my_config.h" #include "generic_file.hpp" #include "thread_cancellation.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the null_file class implements the /dev/null behavior /// this is a generic_file implementation that emulate the /// comportment of the /dev/null special file. /// all that is writen to is lost, and nothing can be read from /// it (empty file). This is a completed implementation all /// call are consistent. class null_file : public generic_file, public thread_cancellation { public : null_file(gf_mode m) : generic_file(m) { set_offset(0); }; null_file(const null_file & ref) = default; null_file(null_file && ref) noexcept = default; null_file & operator = (const null_file & ref) = default; null_file & operator = (null_file && ref) noexcept = default; ~null_file() = default; virtual bool skippable(skippability direction, const infinint & amount) override { return true; }; virtual bool skip(const infinint &pos) override { set_offset(pos); return true; }; virtual bool skip_to_eof() override { offset = max_offset; return true; }; virtual bool skip_relative(signed int x) override { return set_rel_offset(x); }; virtual bool truncatable(const infinint & pos) const override { return true; }; virtual infinint get_position() const override { return offset; }; protected : virtual void inherited_read_ahead(const infinint & amount) override {}; virtual U_I inherited_read(char *a, U_I size) override { #ifdef MUTEX_WORKS check_self_cancellation(); #endif return 0; }; virtual void inherited_write(const char *a, U_I siz) override { #ifdef MUTEX_WORKS check_self_cancellation(); #endif set_offset(offset + siz); }; virtual void inherited_truncate(const infinint & pos) override { if(pos < offset) offset = pos; }; virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override {}; private: infinint offset; infinint max_offset; void set_offset(const infinint & x) { if(x > max_offset) max_offset = x; offset = x; } bool set_rel_offset(signed int x) { if(x >= 0) { set_offset(offset + x); return true; } else // x < 0 { infinint tmp = -x; if(tmp > offset) { offset = 0; return false; } else { offset -= tmp; return true; } } } }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/user_interaction5.cpp0000644000175000017500000001360514636067146015511 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_FCNTL_H #include #endif #if STDC_HEADERS #include #endif } // end extern "C" #include #include "user_interaction5.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "integers.hpp" #include "deci.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar5 { static void remove_last_char_if_equal_to(char c, string & s); user_interaction::user_interaction() { use_listing = false; use_dar_manager_show_files = false; use_dar_manager_contents = false; use_dar_manager_statistics = false; use_dar_manager_show_version = false; at_once = 0; count = 0; } void user_interaction::listing(const std::string & flag, const std::string & perm, const std::string & uid, const std::string & gid, const std::string & size, const std::string & date, const std::string & filename, bool is_dir, bool has_children) { // stupid code to stop having compiler complaining against unused arguments throw Elibcall("user_interaction::listing", libdar::tools_printf("Not overwritten listing() method called with: (%S, %S, %S, %S, %S, %S, %S, %s, %s)", &flag, &perm, &uid, &gid, &size, &date, &filename, is_dir ? "true" : "false", has_children ? "true" : "false")); } void user_interaction::dar_manager_show_files(const string & filename, bool data_change, bool ea_change) { throw Elibcall("user_interaction::dar_manager_show_files", "Not overwritten dar_manager_show_files() method has been called!"); } void user_interaction::dar_manager_contents(U_I number, const string & chemin, const string & archive_name) { throw Elibcall("user_interaction::dar_manager_contents", "Not overwritten dar_manager_contents() method has been called!"); } void user_interaction::dar_manager_statistics(U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea) { throw Elibcall("user_interaction::dar_manager_statistics", "Not overwritten dar_manager_statistics() method has been called!"); } void user_interaction::dar_manager_show_version(U_I number, const string & data_date, const string & data_presence, const string & ea_date, const string & ea_presence) { throw Elibcall("user_interaction::dar_manager_show_version", "Not overwritten dar_manager_show_version() method has been called!"); } void user_interaction::inherited_message(const std::string & message) { if(at_once > 0) { U_I c = 0, max = message.size(); while(c < max) { if(message[c] == '\n') count++; c++; } count++; // for the implicit \n at end of message if(count >= at_once) { count = 0; (void)pause(libdar::dar_gettext("Continue? ")); } } inherited_warning(message); } bool user_interaction::inherited_pause(const string & message) { bool ret = true; try { pause(message); } catch(Euser_abort & e) { ret = false; } return ret; } string user_interaction::inherited_get_string(const string & message, bool echo) { return get_string(message, echo); } secu_string user_interaction::inherited_get_secu_string(const string & message, bool echo) { return get_secu_string(message, echo); } void user_interaction::printf(const char *format, ...) { va_list ap; va_start(ap, format); string output = ""; try { output = libdar::tools_vprintf(format, ap); } catch(...) { va_end(ap); throw; } va_end(ap); remove_last_char_if_equal_to('\n', output); message(output); } shared_ptr user_interaction5_clone_to_shared_ptr(user_interaction & dialog) { user_interaction *tmp = dialog.clone(); if(tmp == nullptr) throw Ememory("archive::clone_to_shared_ptr"); // the pointed to object is newly created here // and never used afterward, thus passing it // as is to a shared_ptr does not lead to an // undefined behavior return shared_ptr(tmp); } static void remove_last_char_if_equal_to(char c, string & s) { if(s[s.size()-1] == c) s = string(s.begin(), s.begin()+(s.size() - 1)); } } // end of namespace dar-2.7.15/src/libdar/archive_version.cpp0000644000175000017500000000733614636066467015246 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "archive_version.hpp" #include "erreurs.hpp" #include "tools.hpp" using namespace std; // value read from file when the file is full of '\0' #define EMPTY_VALUE (48*256 + 48) #define ARCHIVE_VER_SIZE 4 #define OLD_ARCHIVE_VER_SIZE 3 namespace libdar { const archive_version empty_archive_version() { return archive_version(EMPTY_VALUE); } archive_version::archive_version(U_16 x, unsigned char x_fix) { if(x > EMPTY_VALUE) throw Efeature(gettext("Archive version too high, use a more recent version of libdar")); else { version = x; fix = x_fix; } } void archive_version::dump(generic_file & f) const { unsigned char tmp[ARCHIVE_VER_SIZE]; tmp[0] = version / 256; tmp[1] = version % 256; tmp[2] = fix; tmp[3] = '\0'; for(U_I i = 0; i < 3; ++i) tmp[i] = to_char(tmp[i]); (void)f.write((char *)tmp, sizeof(tmp)); } void archive_version::read(generic_file & f) { unsigned char tmp[OLD_ARCHIVE_VER_SIZE]; U_I lu = f.read((char *)tmp, sizeof(tmp)); if(lu < sizeof(tmp)) throw Erange("archive_version::read", gettext("Reached End of File while reading archive version")); for(U_I i = 0; i < 2; ++i) tmp[i] = to_digit(tmp[i]); version = tmp[0]*256+tmp[1]; // little endian used in file // sanity checks if(version < 8) { if(tmp[2] != '\0') throw Erange("archive_version::read", gettext("Unexpected value while reading archive version")); } else // version >= 8 { fix = to_digit(tmp[2]); lu = f.read((char *)tmp, 1); if(lu < 1) throw Erange("archive_version::read", gettext("Reached premature end of file while reading archive version")); if(tmp[0] != '\0') throw Erange("archive_version::read", gettext("Unexpected value while reading archive version")); } } string archive_version::display() const { string ret = tools_uword2str(version); if(ret.size() < 2) ret = string("0") + ret; if(fix > 0) { ret += "." + tools_uword2str((U_16)(fix)); // this is intentional that only few values may be supported, as the fix number // should not grow very much (fix < 10) between two major releases // so far it was needed only once for more 8 major released (well it did not // this feature did not exist at that time, thus I had to shift by one the // in development code some of the revision test, which took time and was error prone). // so, never again. } return ret; } unsigned char archive_version::to_digit(unsigned char val) { if(val < 48) return val + 208; else return val - 48; } unsigned char archive_version::to_char(unsigned char val) { if(val < 208) return val + 48; else return val - 208; } } // end of namespace dar-2.7.15/src/libdar/mask_list.hpp0000644000175000017500000000657614636067146014053 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mask_list.hpp /// \brief here lies a mask that selects files present in a given list /// \ingroup API /// /// The mask_list classes defined here is to be used for filtering files /// in the libdar API calls. #ifndef MASK_LIST_HPP #define MASK_LIST_HPP #include "../my_config.h" #include "mask.hpp" #include namespace libdar { /// \addtogroup API /// @{ /// the mask_list class, matches string that are present in a given file /// the given file must contain one entry per line (thus no carriage return /// is allowed in a given entry). Note that the file listed in the /// file may have a relative path or an absolute path. class mask_list : public mask { public: /// the constructor /// \param[in] filename_list_st is the path to the file listing the /// filename to select for the operation /// \param[in] case_sensit whether comparison is case sensitive or not /// \param[in] prefix add this prefix to relative paths of the list. The /// prefix should be either absolute, or path::FAKE_ROOT (if the operation /// does not involve an fs_root - testing, listing, merging operations for example) /// \param[in] include whether the mask_list is used for file inclusion or file exclusion mask_list(const std::string & filename_list_st, bool case_sensit, const path & prefix, bool include); mask_list(const mask_list & ref) = default; mask_list(mask_list && ref) = default; mask_list & operator = (const mask_list & ref) = default; mask_list & operator = (mask_list && ref) noexcept = default; ~mask_list() = default; /// inherited from the mask class virtual bool is_covered(const std::string & expression) const override; /// inherited from the mask class virtual mask *clone() const override { return new (std::nothrow) mask_list(*this); }; /// routing only necessary for doing some testing U_I size() const { return contenu.size(); }; /// output the listing content virtual std::string dump(const std::string & prefix) const override; private: std::deque contenu; U_I taille; bool case_s; bool including; // mask is used for including files (not for excluding files) }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mycurl_param_list.cpp0000644000175000017500000000563114636066467015602 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "mycurl_param_list.hpp" #include #include "erreurs.hpp" #include "tools.hpp" using namespace std; namespace libdar { #if LIBCURL_AVAILABLE void mycurl_param_list::clear(CURLoption opt) { map >::iterator it = element_list.find(opt); if(it != element_list.end()) element_list.erase(it); reset_read(); } bool mycurl_param_list::read_next(CURLoption & opt) { if(cursor == element_list.end()) return false; opt = cursor->first; return true; } list mycurl_param_list::update_with(const mycurl_param_list & wanted) { list ret; std::map >::const_iterator wit; // will be used on wanted std::map >::iterator mit; // will be used on "this" // adding changed parameters wit = wanted.element_list.begin(); while(wit != wanted.element_list.end()) { if(!wit->second) throw Erange("mycurl_param_list", "empty value in mycurl_param_list"); mit = element_list.find(wit->first); if(mit == element_list.end() // entry not found || ! mit->second // found but associated to no value (!) || *(mit->second) != *(wit->second)) // found, associated to a value but different from the one in wanted { add_clone(wit->first, *(wit->second)); ret.push_back(wit->first); } ++wit; } reset_read(); return ret; } void mycurl_param_list::copy_from(const mycurl_param_list & ref) { map >::const_iterator it = ref.element_list.begin(); while(it != ref.element_list.end()) { if(it->second) element_list[it->first] = it->second->clone(); else throw SRC_BUG; ++it; } reset_read(); } #endif } // end of namespace dar-2.7.15/src/libdar/data_dir.hpp0000644000175000017500000001203314636066467013622 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file data_dir.hpp /// \brief classes used to store directory tree information in dar_manager databases /// \ingroup Private #ifndef DATA_DIR_HPP #define DATA_DIR_HPP #include "../my_config.h" #include #include #include #include "infinint.hpp" #include "generic_file.hpp" #include "user_interaction.hpp" #include "path.hpp" #include "cat_directory.hpp" #include "cat_inode.hpp" #include "cat_detruit.hpp" #include "data_tree.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the data_dir class inherits from data_tree and holds the directory tree's parent relationship class data_dir : public data_tree { public: data_dir(const std::string &name); data_dir(generic_file &f, unsigned char db_version); //< does not read signature data_dir(const data_tree & ref); data_dir(const data_dir & ref); data_dir(data_dir && ref) = default; data_dir & operator = (const data_dir & ref) { rejetons.clear(); return *this; }; data_dir & operator = (data_dir && ref) noexcept = default; ~data_dir(); virtual void dump(generic_file & f) const override; //< write signature followed by data constructor will read void add(const cat_inode *entry, const archive_num & archive); void add(const cat_detruit *entry, const archive_num & archive); const data_tree *read_child(const std::string & name) const; void read_all_children(std::vector & fils) const; void finalize_except_self(const archive_num & archive, const datetime & deleted_date, const archive_num & ignore_archives_greater_or_equal); // inherited methods virtual bool check_order(user_interaction & dialog, const path & current_path, bool & initial_warn) const override; virtual void finalize(const archive_num & archive, const datetime & deleted_date, const archive_num & ignore_archives_greater_or_equal) override; virtual bool remove_all_from(const archive_num & archive_to_remove, const archive_num & last_archive) override; /// list the most recent files owned by that archive (or by any archive if num == 0) void show(database_listing_show_files_callback callback, void *tag, archive_num num, std::string marge = "") const; virtual void apply_permutation(archive_num src, archive_num dst) override; virtual void skip_out(archive_num num) override; virtual void compute_most_recent_stats(std::deque & data, std::deque & ea, std::deque & total_data, std::deque & total_ea) const override; virtual char obj_signature() const override { return signature(); }; static char signature() { return 'd'; }; virtual bool fix_corruption() override; // inherited from data_tree /// lookup routine to find a pointer to the dat_tree object corresponding to the given path /// \param[in] chemin is the path to look for /// \param[out] ptr is a pointer to the looked node, if found /// \return true if a node could be found in the database bool data_tree_find(path chemin, const data_tree *& ptr) const; /// add a directory to the dat_dir void data_tree_update_with(const cat_directory *dir, archive_num archive); /// read a signature and then run the data_dir constructor but aborts if not a data_dir /// \note the constructors of data_tree and data_dir do not read the signature /// because its purpose it to know whether the following is a data_dir or data_tree /// dump() result. static data_dir *data_tree_read(generic_file & f, unsigned char db_version); private: std::deque rejetons; //< subdir and subfiles of the current dir void add_child(data_tree *fils); //< "this" is now responsible of "fils" disalocation void remove_child(const std::string & name); data_tree *find_or_addition(const std::string & name, bool is_dir, const archive_num & archive); /// read signature and depening on it run data_tree or data_dir constructor static data_tree *read_next_in_list_from_file(generic_file & f, unsigned char db_version); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/entrepot_libcurl.hpp0000644000175000017500000001042214636067146015422 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file entrepot_libcurl.hpp /// \brief defines the implementation for remote filesystem entrepot using libcurl /// \ingroup API #ifndef ENTREPOT_LIBCURL_HPP #define ENTREPOT_LIBCURL_HPP #include "../my_config.h" extern "C" { } #include #include #include "entrepot.hpp" #include "secu_string.hpp" #include "mycurl_protocol.hpp" namespace libdar { /// \addtogroup API /// @{ /// for managing archive into a remote repository class entrepot_libcurl : public entrepot { public: entrepot_libcurl(const std::shared_ptr & dialog, ///< for user interaction mycurl_protocol proto, ///< network protocol to use const std::string & login, ///< user login on remote host const secu_string & password, ///< user password on remote host (empty for file auth or user interaction) const std::string & host, ///< the remote server to connect to const std::string & port, ///< TCP/UDP port to connec to (empty string for default) bool auth_from_file, ///< whether to check $HOME/.netrc for password const std::string & sftp_pub_keyfile, ///< where to fetch the public key (sftp only) const std::string & sftp_prv_keyfile, ///< where to fetch the private key (sftp only) const std::string & sftp_known_hosts, ///< location of the known_hosts file (empty string to disable this security check) U_I waiting_time, ///< time in second to wait before retrying in case of network error bool verbose = false ///< whether to have verbose messages from libcurl ); entrepot_libcurl(const entrepot_libcurl & ref) = default; entrepot_libcurl(entrepot_libcurl && ref) noexcept = default; entrepot_libcurl & operator = (const entrepot_libcurl & ref) = default; entrepot_libcurl & operator = (entrepot_libcurl && ref) noexcept = default; ~entrepot_libcurl() throw () {}; // inherited from class entrepot virtual void set_location(const path & chemin) override; virtual void set_root(const path & p_root) override; virtual path get_full_path() const override; /// \note this is expected to have a double slash after the host:port /// like ftp://www.some.where:8021//tmp/sub/dir5A virtual std::string get_url() const override; virtual const path & get_location() const override; virtual const path & get_root() const override; virtual void read_dir_reset() const override; virtual bool read_dir_next(std::string & filename) const override; virtual entrepot *clone() const override { return new (std::nothrow) entrepot_libcurl(*this); }; protected: // inherited from class entrepot virtual fichier_global *inherited_open(const std::shared_ptr & dialog, const std::string & filename, gf_mode mode, bool force_permission, U_I permission, bool fail_if_exists, bool erase) const override; virtual void inherited_unlink(const std::string & filename) const override; virtual void read_dir_flush() override; private: #if defined ( LIBCURL_AVAILABLE ) && defined ( LIBTHREADAR_AVAILABLE ) class i_entrepot_libcurl; std::shared_ptr pimpl; #endif }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/list_entry.cpp0000644000175000017500000002027314636067146014242 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "tools.hpp" #include "list_entry.hpp" #include "tools.hpp" using namespace std; namespace libdar { unsigned char list_entry::get_removed_type() const { if(is_removed_entry()) { if(target.size() != 1) throw SRC_BUG; return target[0]; } else return '!'; } string list_entry::get_data_flag() const { switch(data_status) { case saved_status::saved: if(dirty) return gettext("[DIRTY]"); else return gettext("[Saved]"); case saved_status::inode_only: return gettext("[Inode]"); case saved_status::fake: return gettext("[InRef]"); case saved_status::not_saved: return "[ ]"; case saved_status::delta: return "[Delta]"; default: throw SRC_BUG; } } string list_entry::get_ea_flag() const { switch(ea_status) { case ea_saved_status::full: return gettext("[Saved]"); case ea_saved_status::fake: return gettext("[InRef]"); case ea_saved_status::partial: return "[ ]"; case ea_saved_status::none: return " "; case ea_saved_status::removed: return "[Suppr]"; default: throw SRC_BUG; } } string list_entry::get_fsa_flag() const { string ret = ""; if(fsa_status != fsa_saved_status::none) { fsa_scope sc = get_fsa_scope(); bool upper = fsa_status == fsa_saved_status::full; ret = fsa_scope_to_string(upper, sc); while(ret.size() < 3) ret += "-"; ret = "[" + ret + "]"; } else ret = "[---]"; return ret; } string list_entry::get_uid(bool try_resolving_name) const { if(try_resolving_name) return tools_name_of_uid(uid); else return deci(uid).human(); } string list_entry::get_gid(bool try_resolving_name) const { if(try_resolving_name) return tools_name_of_gid(gid); else return deci(gid).human(); } string list_entry::get_perm() const { return tools_get_permission_string(type, perm, hard_link); } string list_entry::get_last_access() const { return last_access.is_null() ? "" : tools_display_date(last_access); } string list_entry::get_last_modif() const { if(!is_removed_entry()) return last_modif.is_null() ? "" : tools_display_date(last_modif); else return ""; } string list_entry::get_last_change() const { return last_change.is_null() ? "" : tools_display_date(last_change); } string list_entry::get_removal_date() const { if(is_removed_entry()) return last_modif.is_null() ? "Unknown date" : tools_display_date(last_modif); else return ""; } time_t list_entry::get_last_modif_s() const { if(!is_removed_entry()) return datetime2time_t(last_modif); else return 0; } time_t list_entry::get_removal_date_s() const { if(is_removed_entry()) return datetime2time_t(last_modif); else return 0; } string list_entry::get_file_size(bool size_in_bytes) const { if(size_in_bytes) return deci(file_size).human(); else return tools_display_integer_in_metric_system(file_size , "o", true); } string list_entry::get_compression_ratio() const { if((is_file() && has_data_present_in_the_archive()) || is_dir()) return tools_get_compression_ratio(storage_size_for_data, file_size, compression_algo != compression::none || is_sparse() || is_dir() || get_data_status() == saved_status::delta); else return ""; } string list_entry::get_major() const { return tools_int2str(major); } string list_entry::get_minor() const { return tools_int2str(minor); } string list_entry::get_compression_ratio_flag() const { string ret = get_compression_ratio(); if(ret.size() == 0) return "[-----]"; else return "[" + ret + "]"; } string list_entry::get_delta_flag() const { if(!is_file()) return "[-]"; else { if(has_delta_signature()) return "[D]"; else return "[ ]"; } } bool list_entry::get_archive_offset_for_data(U_64 & val) const { return tools_infinint2U_64(offset_for_data, val) && !offset_for_data.is_zero(); } bool list_entry::get_storage_size_for_data(U_64 & val) const { return tools_infinint2U_64(storage_size_for_data, val) && !storage_size_for_data.is_zero(); } bool list_entry::get_archive_offset_for_EA(U_64 & val) const { return tools_infinint2U_64(offset_for_EA, val) && !offset_for_EA.is_zero(); } bool list_entry::get_storage_size_for_EA(U_64 & val) const { return tools_infinint2U_64(storage_size_for_EA, val) && !storage_size_for_EA.is_zero(); } bool list_entry::get_archive_offset_for_FSA(U_64 & val) const { return tools_infinint2U_64(offset_for_FSA, val) && !offset_for_FSA.is_zero(); } bool list_entry::get_storage_size_for_FSA(U_64 & val) const { return tools_infinint2U_64(storage_size_for_FSA, val) && !storage_size_for_FSA.is_zero(); } string list_entry::get_storage_size_for_data(bool size_in_bytes) const { if(size_in_bytes) return deci(storage_size_for_data).human(); else return tools_display_integer_in_metric_system(storage_size_for_data, "o", true); } bool list_entry::get_ea_read_next(std::string & key) const { if(it_ea != ea.end()) { key = *it_ea; ++it_ea; return true; } else return false; } void list_entry::set_removal_date(const datetime & val) { if(!is_removed_entry()) throw SRC_BUG; last_modif = val; // we recycle last_modif for this purpose // when the entry is a "removed" one } void list_entry::set_removed_type(unsigned char val) { if(!is_removed_entry()) throw SRC_BUG; target.clear(); target += val; if(target.size() != 1) throw SRC_BUG; } void list_entry::set_ea(const ea_attributs & arg) { string key, val; ea.clear(); arg.reset_read(); while(arg.read(key, val)) ea.push_back(key); // we ignore val get_ea_reset_read(); } void list_entry::set_data_crc(const crc & ptr) { data_crc = ptr.crc2str(); } void list_entry::set_delta_patch_base_crc(const crc & ptr) { patch_base_crc = ptr.crc2str(); } void list_entry::set_delta_patch_result_crc(const crc & ptr) { patch_result_crc = ptr.crc2str(); } void list_entry::clear() { my_name = ""; hard_link = false; type = ' '; uid = 0; gid = 0; perm = 0; last_access.nullify(); last_modif.nullify(); data_status = saved_status::saved; ea_status = ea_saved_status::none; last_change.nullify(); fsa_status = fsa_saved_status::none; file_size = 0; sparse_file = false; compression_algo = compression::none; dirty = false; target = ""; major = 0; minor = 0; slices.clear(); delta_sig = false; offset_for_data = 0; storage_size_for_data = 0; offset_for_EA = 0; storage_size_for_EA = 0; offset_for_FSA = 0; storage_size_for_FSA = 0; ea.clear(); it_ea = ea.begin(); etiquette = 0; data_crc = ""; patch_base_crc = ""; patch_result_crc = ""; empty_dir = false; } time_t list_entry::datetime2time_t(const datetime & val) { time_t ret = 0; time_t unused; (void) val.get_value(ret, unused, datetime::tu_second); return ret; } } // end of namespace dar-2.7.15/src/libdar/tuyau_global.cpp0000644000175000017500000000635314636066467014545 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "tuyau_global.hpp" using namespace std; namespace libdar { tuyau_global::tuyau_global(const std::shared_ptr & dialog, fichier_global *x_ptr): fichier_global(dialog, gf_read_only) { if(x_ptr == nullptr) throw SRC_BUG; // now we know the provided pointer is valid // we can set the proper mode in place of gf_read_only // passed to fichier_global constructor above set_mode(x_ptr->get_mode()); ptr = x_ptr; current_pos = 0; } bool tuyau_global::skip(const infinint & pos) { if(pos < current_pos) return false; else { infinint amount = pos - current_pos; U_I step_asked = 0; U_I step_done = 0; while(!amount.is_zero() && step_done == step_asked) { step_asked = 0; amount.unstack(step_asked); step_done = read_and_drop(step_asked); current_pos += step_done; } return step_done == step_asked; } } bool tuyau_global::skip_to_eof() { U_I read; do { read = read_and_drop(buffer_size); current_pos += infinint(read); } while(read == buffer_size); return true; } bool tuyau_global::skip_relative(S_I x) { U_I read; if(x < 0) return false; read = read_and_drop(U_I(x)); current_pos += read; return read == U_I(x); } U_I tuyau_global::fichier_global_inherited_write(const char *a, U_I size) { ptr->write(a, size); current_pos += size; return size; } bool tuyau_global::fichier_global_inherited_read(char *a, U_I size, U_I & read, std::string & message) { read = ptr->read(a, size); current_pos += read; return true; } U_I tuyau_global::read_and_drop(U_I bytes) { U_I read = 0; U_I min; U_I incr; while(bytes > 0) { min = bytes > buffer_size ? buffer_size : bytes; incr = ptr->read(buffer, min); // we used read, to let the object handle to exception // contexts and if fixed continue the read_and_drop // operation normally read += incr; if(incr < min) // reached eof bytes = 0; // we force the loop to end else bytes -= incr; } return read; } void tuyau_global::detruit() { if(ptr != nullptr) { delete ptr; ptr = nullptr; } } } // end of namespace dar-2.7.15/src/libdar/gf_mode.cpp0000644000175000017500000000312114636066467013444 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "gf_mode.hpp" #include "erreurs.hpp" using namespace std; namespace libdar { const char* generic_file_get_name(gf_mode mode) { const char *ret = nullptr; switch(mode) { case gf_read_only: ret = gettext("read only"); break; case gf_write_only: ret = gettext("write only"); break; case gf_read_write: ret = gettext("read and write"); break; default: throw SRC_BUG; } return ret; } } // end of namespace dar-2.7.15/src/libdar/mem_block.cpp0000644000175000017500000000640714636066467014006 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #ifdef HAVE_STRING_H #include #endif } #include "mem_block.hpp" #include "erreurs.hpp" using namespace std; namespace libdar { mem_block::mem_block(U_I size) { data = nullptr; resize(size); } mem_block::mem_block(mem_block && ref) noexcept { try { data = nullptr; move_from(move(ref)); } catch(...) { // noexcept } } mem_block::~mem_block() { if(data != nullptr) delete [] data; } mem_block & mem_block::operator = (mem_block && ref) noexcept { try { move_from(move(ref)); } catch(...) { // noexcept } return *this; } void mem_block::resize(U_I size) { if(data != nullptr) { delete [] data; data = nullptr; } if(size > 0) { data = new (nothrow) char[size]; if(data == nullptr) throw Ememory("mem_block::mem_block"); } alloc_size = size; data_size = 0; read_cursor = 0; write_cursor = 0; } U_I mem_block::read(char *a, U_I size) { if(data_size < read_cursor) throw SRC_BUG; else { U_I remains = data_size - read_cursor; U_I amount = size < remains ? size : remains; memcpy(a, data + read_cursor, amount); read_cursor += amount; return amount; } } U_I mem_block::write(const char *a, U_I size) { if(alloc_size < write_cursor) throw SRC_BUG; else { U_I remains = alloc_size - write_cursor; U_I amount = size < remains ? size : remains; memcpy(data + write_cursor, a, amount); write_cursor += amount; if(data_size < write_cursor) data_size = write_cursor; return amount; } } void mem_block::rewind_read(U_I offset) { if(offset > data_size) throw Erange("mem_block::reset_read", "offset out of range for mem_block"); read_cursor = offset; } void mem_block::set_data_size(U_I size) { if(size > alloc_size) throw SRC_BUG; data_size = size; if(read_cursor < size) read_cursor = size; if(write_cursor < size) write_cursor = size; } void mem_block::move_from(mem_block && ref) { swap(data, ref.data); alloc_size = move(ref.alloc_size); data_size = move(ref.data_size); read_cursor = move(ref.read_cursor); write_cursor = move(ref.write_cursor); } } // end of namespace dar-2.7.15/src/libdar/libdar5.hpp0000644000175000017500000005627414640024724013375 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file libdar5.hpp /// \brief backward compatibility to libdar API 5 /// \ingroup API5 #ifndef LIBDAR5_HPP #define LIBDAR5_HPP #include "../my_config.h" extern "C" { #if MUTEX_WORKS #if HAVE_PTHREAD_H #include #endif #endif } #include #include "compressor.hpp" #include "path.hpp" #include "mask.hpp" #include "mask_list.hpp" #include "integers.hpp" #include "infinint.hpp" #include "statistics.hpp" #include "user_interaction.hpp" #include "user_interaction_callback5.hpp" #include "deci.hpp" #include "archive5.hpp" #include "crypto.hpp" #include "thread_cancellation.hpp" #include "compile_time_features.hpp" #include "capabilities.hpp" #include "entrepot_libcurl5.hpp" #include "fichier_local.hpp" #include "entrepot_local.hpp" #include "data_tree.hpp" #include "database5.hpp" #include "tuyau.hpp" #include "archive_aux.hpp" #include "tools.hpp" /// \addtogroup API5 /// @{ /// The following macro are used in the "exception" argument of the *_noexcept() functions #define LIBDAR_XXXXXXXX /// normal return no exception has been thrown #define LIBDAR_NOEXCEPT 0 /// memory has been exhausted #define LIBDAR_EMEMORY 1 /// internal bug error. #define LIBDAR_EBUG 2 /// division by zero or other arithmetic error #define LIBDAR_EINFININT 3 /// limitint overflow #define LIBDAR_ELIMITINT 4 /// range error #define LIBDAR_ERANGE 5 /// decimal representation error #define LIBDAR_EDECI 6 /// feature not (yet) implemented #define LIBDAR_EFEATURE 7 /// hardware failure #define LIBDAR_EHARDWARE 8 /// user has aborted the operation #define LIBDAR_EUSER_ABORT 9 /// data inconsistency, error concerning the treated data #define LIBDAR_EDATA 10 /// inter slice script failure #define LIBDAR_ESCRIPT 11 /// libdar invalid call (wrong argument given to call, etc.) #define LIBDAR_ELIBCALL 12 /// unknown error #define LIBDAR_UNKNOWN 13 /// feature not activated at compilation time #define LIBDAR_ECOMPILATION 14 /// thread cancellation has been requested #define LIBDAR_THREAD_CANCEL 15 /// @} /// libdar5 namespace encapsulate all libdar symbols namespace libdar5 { /// \addtogroup API5 /// @{ // from integers.hpp using libdar::U_8; using libdar::U_16; using libdar::U_32; using libdar::U_64; using libdar::U_I; using libdar::S_8; using libdar::S_16; using libdar::S_32; using libdar::S_64; using libdar::S_I; // from infinint.hpp using libdar::infinint; // from erreurs.hpp using libdar::Egeneric; using libdar::Ememory; using libdar::Esecu_memory; using libdar::Ebug; using libdar::Einfinint; using libdar::Elimitint; using libdar::Edeci; using libdar::Erange; using libdar::Efeature; using libdar::Ehardware; using libdar::Edata; using libdar::Euser_abort; using libdar::Escript; using libdar::Elibcall; using libdar::Ecompilation; using libdar::Ethread_cancel; using libdar::Esystem; // from secu_string.hpp using libdar::secu_string; // from path.hpp using libdar::path; // from compressor.hpp using libdar::compression; constexpr compression none = compression::none; constexpr compression gzip = compression::gzip; constexpr compression bzip2 = compression::bzip2; constexpr compression lzo = compression::lzo; constexpr compression xz = compression::xz; constexpr compression lzo1x_1_15 = compression::lzo1x_1_15; constexpr compression lzo1x_1 = compression::lzo1x_1; inline compression char2compression(char a) { return libdar::char2compression(a); } inline char compression2char(compression c) { return libdar::compression2char(c); } inline std::string compression2string(compression c) { return libdar::compression2string(c); } inline compression string2compression(const std::string & a) { return libdar::string2compression(a); } // from crypto.hpp using libdar::crypto_algo; constexpr crypto_algo crypto_none = crypto_algo::none; constexpr crypto_algo crypto_scrambling = crypto_algo::scrambling; constexpr crypto_algo crypto_blowfish = crypto_algo::blowfish; constexpr crypto_algo crypto_aes256 = crypto_algo::aes256; constexpr crypto_algo crypto_twofish256 = crypto_algo::twofish256; constexpr crypto_algo crypto_serpent256 = crypto_algo::serpent256; constexpr crypto_algo crypto_camellia256 = crypto_algo::camellia256; using libdar::signator; // from statistics.hpp using libdar::statistics; // from list_entry.hpp using libdar::list_entry; // from entree_stats.hpp using libdar::entree_stats; // from archive.hpp // using libdar::archive; // from capabilities.hpp using libdar::capa_status; constexpr capa_status capa_set = libdar::capa_status::capa_set; constexpr capa_status capa_clear = libdar::capa_status::capa_clear; constexpr capa_status capa_unknown = libdar::capa_status::capa_unknown; // from mask.hpp using libdar::mask; using libdar::bool_mask; using libdar::simple_mask; using libdar::regular_mask; using libdar::not_mask; using libdar::et_mask; using libdar::ou_mask; using libdar::simple_path_mask; using libdar::same_path_mask; using libdar::exclude_dir_mask; // from deci.hpp using libdar::deci; // from thread_cancellation using libdar::thread_cancellation; // from data_tree.hpp using libdar::archive_num; // from criterium.hpp using libdar::criterium; using libdar::crit_in_place_is_inode; using libdar::crit_in_place_is_dir; using libdar::crit_in_place_is_file; using libdar::crit_in_place_is_hardlinked_inode; using libdar::crit_in_place_is_new_hardlinked_inode; using libdar::crit_in_place_data_more_recent; using libdar::crit_in_place_data_more_recent_or_equal_to; using libdar::crit_in_place_data_bigger; using libdar::crit_in_place_data_saved; using libdar::crit_in_place_data_dirty; using libdar::crit_in_place_data_sparse; using libdar::crit_in_place_has_delta_sig; using libdar::crit_in_place_EA_present; using libdar::crit_in_place_EA_more_recent; using libdar::crit_in_place_EA_more_recent_or_equal_to; using libdar::crit_in_place_more_EA; using libdar::crit_in_place_EA_bigger; using libdar::crit_in_place_EA_saved; using libdar::crit_same_type; using libdar::crit_not; using libdar::crit_and; using libdar::crit_or; using libdar::crit_invert; // from crit_action.hpp using libdar::over_action_data; constexpr over_action_data data_preserve = libdar::over_action_data::data_preserve; constexpr over_action_data data_overwrite = libdar::over_action_data::data_overwrite; constexpr over_action_data data_preserve_mark_already_saved = libdar::over_action_data::data_preserve_mark_already_saved; constexpr over_action_data data_overwrite_mark_already_saved = libdar::over_action_data::data_overwrite_mark_already_saved; constexpr over_action_data data_remove = libdar::over_action_data::data_remove; constexpr over_action_data data_undefined = libdar::over_action_data::data_undefined; constexpr over_action_data data_ask = libdar::over_action_data::data_ask; using libdar::over_action_ea; constexpr over_action_ea EA_preserve = libdar::over_action_ea::EA_preserve; constexpr over_action_ea EA_overwrite = libdar::over_action_ea::EA_overwrite; constexpr over_action_ea EA_clear = libdar::over_action_ea::EA_clear; constexpr over_action_ea EA_preserve_mark_already_saved = libdar::over_action_ea::EA_preserve_mark_already_saved; constexpr over_action_ea EA_overwrite_mark_already_saved = libdar::over_action_ea::EA_overwrite_mark_already_saved; constexpr over_action_ea EA_merge_preserve = libdar::over_action_ea::EA_merge_preserve; constexpr over_action_ea EA_merge_overwrite = libdar::over_action_ea::EA_merge_overwrite; constexpr over_action_ea EA_undefined = libdar::over_action_ea::EA_undefined; constexpr over_action_ea EA_ask = libdar::over_action_ea::EA_ask; using libdar::crit_action; using libdar::crit_constant_action; using libdar::testing; using libdar::crit_chain; // from mask_list.hpp using libdar::mask_list; // from hash_fichier.hpp using libdar::hash_algo; constexpr hash_algo hash_none = hash_algo::none; constexpr hash_algo hash_md5 = hash_algo::md5; constexpr hash_algo hash_sha1 = hash_algo::sha1; constexpr hash_algo hash_sha512= hash_algo::sha512; // from fsa_family.hpp using libdar::fsa_family; constexpr fsa_family fsaf_hfs_plus = fsa_family::fsaf_hfs_plus; constexpr fsa_family fsaf_linux_extX = fsa_family::fsaf_linux_extX; using libdar::fsa_nature; constexpr fsa_nature fsan_unset = fsa_nature::fsan_unset; constexpr fsa_nature fsan_creation_date = fsa_nature::fsan_creation_date; constexpr fsa_nature fsan_append_only = fsa_nature::fsan_append_only; constexpr fsa_nature fsan_compressed = fsa_nature::fsan_compressed; constexpr fsa_nature fsan_no_dump = fsa_nature::fsan_no_dump; constexpr fsa_nature fsan_immutable = fsa_nature::fsan_immutable; constexpr fsa_nature fsan_data_journaling = fsa_nature::fsan_data_journaling; constexpr fsa_nature fsan_secure_deletion = fsa_nature::fsan_secure_deletion; constexpr fsa_nature fsan_no_tail_merging = fsa_nature::fsan_no_tail_merging; constexpr fsa_nature fsan_undeletable = fsa_nature::fsan_undeletable; constexpr fsa_nature fsan_noatime_update = fsa_nature::fsan_noatime_update; constexpr fsa_nature fsan_synchronous_directory = fsa_nature::fsan_synchronous_directory; constexpr fsa_nature fsan_synchronous_udpdate = fsa_nature::fsan_synchronous_update; constexpr fsa_nature fsan_top_of_dir_hierarchy = fsa_nature::fsan_top_of_dir_hierarchy; using libdar::fsa_scope; inline fsa_scope all_fsa_families() { return libdar::all_fsa_families(); } // from generic_file.hpp using libdar::generic_file; using libdar::gf_mode; constexpr gf_mode gf_read_only = gf_mode::gf_read_only; constexpr gf_mode gf_write_only = gf_mode::gf_write_only; constexpr gf_mode gf_read_write = gf_mode::gf_read_write; // from tools.hpp using libdar::tools_printf; using libdar::tools_getcwd; using libdar::tools_get_extended_size; using libdar::tools_my_atoi; using libdar::tools_octal2int; // from compile_time_features.hpp namespace compile_time { using namespace libdar::compile_time; } // from fichier_local.hpp using libdar::fichier_local; // from mycurl_protocol.hpp using libdar::mycurl_protocol; constexpr mycurl_protocol proto_ftp = mycurl_protocol::proto_ftp; constexpr mycurl_protocol proto_sftp = mycurl_protocol::proto_sftp; inline mycurl_protocol string_to_mycurl_protocol(const std::string & arg) { return libdar::string_to_mycurl_protocol(arg); } // from memory_file.hpp using libdar::memory_file; // from entrepot_local using libdar::entrepot_local; // from datetime using libdar::datetime; // from tuyau using libdar::tuyau; // from archive_aux.hpp using libdar::modified_data_detection; using libdar::comparison_fields; constexpr comparison_fields cf_all = comparison_fields::all; constexpr comparison_fields cf_ignore_owner = comparison_fields::ignore_owner; constexpr comparison_fields cf_mtime = comparison_fields::mtime; constexpr comparison_fields cf_inode_type = comparison_fields::inode_type; /// libdar Major version defined at compilation time const U_I LIBDAR_COMPILE_TIME_MAJOR = 5; /// libdar Medium version defined at compilation time const U_I LIBDAR_COMPILE_TIME_MEDIUM = 201; /// libdar Minor version defined at compilation time const U_I LIBDAR_COMPILE_TIME_MINOR = 7; //////////////////////////////////////////////////////////////////////// // LIBDAR INITIALIZATION METHODS // // // // A FUNCTION OF THE get_version*() FAMILY *MUST* BE CALLED // // BEFORE ANY OTHER FUNCTION OF THIS LIBRARY // // // // CLIENT PROGRAM MUST CHECK THAT THE MAJOR NUMBER RETURNED // // BY THIS CALL IS NOT GREATER THAN THE VERSION USED AT COMPILATION // // TIME. IF SO, THE PROGRAM MUST ABORT AND RETURN A WARNING TO THE // // USER TELLING THE DYNAMICALLY LINKED VERSION IS TOO RECENT AND NOT // // COMPATIBLE WITH THIS SOFTWARE. THE MESSAGE MUST INVITE THE USER // // TO UPGRADE HIS SOFTWARE WITH A MORE RECENT VERSION COMPATIBLE WITH // // THIS LIBDAR RELEASE. // //////////////////////////////////////////////////////////////////////// /// return the libdar version, and make libdar initialization (may throw Exceptions) /// It is mandatory to call this function (or another one of the get_version* family) /// \param[out] major the major number of the version /// \param[out] medium the medium number of the version /// \param[out] minor the minor number of the version /// \param[in] init_libgcrypt whether to initialize libgcrypt if not already done (not used if libcrypt is not linked with libdar) /// \note the calling application must match that the major function /// is the same as the libdar used at compilation time. See API tutorial for a /// sample code. extern void get_version(U_I & major, U_I & medium, U_I & minor, bool init_libgcrypt = true); /// return the libdar version, and make libdar initialization (does not throw exceptions) /// It is mandatory to call this function (or another one of the get_version* family) /// \param[out] major the major number of the version /// \param[out] medium the medium number of the version /// \param[out] minor the minor number of the version /// \param[out] exception is to be compared with the LIBDAR_* macro to know whether the call succeeded /// \param[out] except_msg in case exception is not equal to LIBDAR_NOEXCEPT this argument contains /// \param[in] init_libgcrypt whether to initialize libgcrypt if not already done (not used if libcrypt is not linked with libdar) /// a human readable explaination of the error met. /// \note the calling application must match that the major function /// is the same as the libdar used at compilation time. See API tutorial for a /// sample code. extern void get_version_noexcept(U_I & major, U_I & medium, U_I & minor, U_16 & exception, std::string & except_msg, bool init_libgcrypt = true); /////////////////////////////////////////////// // CLOSING/CLEANING LIBDAR // /////////////////////////////////////////////// // while libdar has only a single boolean as global variable // that defines whether the library is initialized or not // it must proceed to mutex, and dependent libraries initializations // (liblzo, libgcrypt, etc.), which is done during the get_version() call // Some library also need to clear some data so the following call // is provided in that aim and must be called when libdar will no more // be used by the application. extern void close_and_clean(); ////////// // WRAPPER FUNCTIONS AROUND archive class methods to trap exceptions and convert them in error code and message // these are intended for C program/programmers not enough confident with C++. // // FOR LIBDAR C++ APPLICATIONS, YOU WOULD RATHER USE THE archive C++ CLASS THAN THESE FOLLOWING WRAPPERS // ////////// /// this is a wrapper around the archive constructor known as the "read" constructor /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern archive* open_archive_noexcept(user_interaction & dialog, const path & chem, const std::string & basename, const std::string & extension, const archive_options_read & options, U_16 & exception, std::string & except_msg); /// this is a wrapper around the archive constructor known as the "create" constructor /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern archive *create_archive_noexcept(user_interaction & dialog, const path & fs_root, const path & sauv_path, const std::string & filename, const std::string & extension, const archive_options_create & options, statistics * progressive_report, U_16 & exception, std::string & except_msg); /// this is a wrapper around the archive constructor known as the "isolate" constructor /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern archive *isolate_archive_noexcept(user_interaction & dialog, archive *ptr, const path &sauv_path, const std::string & filename, const std::string & extension, const archive_options_isolate & options, U_16 & exception, std::string & except_msg); /// this is a wrapper around the archive constructor known as the "merging" constructor /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern archive *merge_archive_noexcept(user_interaction & dialog, const path & sauv_path, archive *ref_arch1, const std::string & filename, const std::string & extension, const archive_options_merge & options, statistics * progressive_report, U_16 & exception, std::string & except_msg); /// this is wrapper around the archive destructor /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern void close_archive_noexcept(archive *ptr, U_16 & exception, std::string & except_msg); /// this is wrapper around the op_extract method /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern statistics op_extract_noexcept(user_interaction & dialog, archive *ptr, const path &fs_root, const archive_options_extract & options, statistics * progressive_report, U_16 & exception, std::string & except_msg); /// this is wrapper around the op_listing method /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern void op_listing_noexcept(user_interaction & dialog, archive *ptr, const archive_options_listing & options, U_16 & exception, std::string & except_msg); /// this is wrapper around the op_diff method /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern statistics op_diff_noexcept(user_interaction & dialog, archive *ptr, const path & fs_root, const archive_options_diff & options, statistics * progressive_report, U_16 & exception, std::string & except_msg); /// this is wrapper around the op_test method /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern statistics op_test_noexcept(user_interaction & dialog, archive *ptr, const archive_options_test & options, statistics * progressive_report, U_16 & exception, std::string & except_msg); /// this is wrapper around the get_children_of method /// check the archive class for details /// for an explaination of the two extra arguments exception and except_msg check /// the get_version_noexcept function extern bool get_children_of_noexcept(user_interaction & dialog, archive *ptr, const std::string & dir, U_16 & exception, std::string & except_msg); /////////////////////////////////////////////// // TOOLS ROUTINES // /////////////////////////////////////////////// /// routine provided to convert std::string to char * /// \param[in] x the string to convert /// \param[out] exception the return status of the call /// \param[out] except_msg the message taken from the caught exception in case of error /// for an explaination of the two last arguments exception and except_msg check /// the get_version_noexcept function /// \return the address of a newly allocated memory /// which must be released calling the "delete []" /// operator when no more needed. /// \return nullptr in case of error extern char *libdar_str2charptr_noexcept(const std::string & x, U_16 & exception, std::string & except_msg); /////////////////////////////////////////////// // THREAD CANCELLATION ROUTINES // /////////////////////////////////////////////// #if MUTEX_WORKS /// thread cancellation activation /// ask that any libdar code running in the thread given as argument be cleanly aborted /// when the execution will reach the next libdar checkpoint /// \param[in] tid is the Thread ID to cancel libdar in /// \param[in] immediate whether to cancel thread immediately or just signal the request to the thread /// \param[in] flag an arbitrary value passed as-is through libdar extern void cancel_thread(pthread_t tid, bool immediate = true, U_64 flag = 0); /// consultation of the cancellation status of a given thread /// \param[in] tid is the tid of the thread to get status about /// \return false if no cancellation has been requested for the given thread extern bool cancel_status(pthread_t tid); /// thread cancellation deactivation /// abort the thread cancellation for the given thread /// \return false if no thread cancellation was under process for that thread /// or if there is no more pending cancellation (thread has already been canceled). extern bool cancel_clear(pthread_t tid); #endif /// @} } // end of namespace #endif dar-2.7.15/src/libdar/delta_sig_block_size.hpp0000644000175000017500000000670314636066467016221 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file delta_sig_block_size.hpp /// \brief structure used to define how to select block size for delta signature /// \ingroup API #ifndef DELTA_SIG_BLOCK_SIZE_HPP #define DELTA_SIG_BLOCK_SIZE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "infinint.hpp" namespace libdar { /// defines how to calculate delta signature block size based of file size to delta sign /// \note the global formula is block_size = multiplicator/divisor*fs_function(filesize) /// \note of course, divisor cannot be null struct delta_sig_block_size { /// defines the function to use to derivate block size from file size enum fs_function_t { fixed, ///< block size is independant from file size linear, ///< block size if proportionnal to file size log2, ///< block size is proportional to log2(file size) square2, ///< block size is proportional to filesize^2 square3 ///< block size if proportional to filesize^3 }; fs_function_t fs_function; //< the function to use to calculate the signature block len infinint multiplier; ///< function dependently used multiplier infinint divisor; ///< function dependently used divisor U_I min_block_len; ///< calculated block len will never be lower than that U_I max_block_len; ///< calculated block len will never be higer than that except if this field is set to zero (disabling this ceiling check) // definitions to help using the struct delta_sig_block_size() { reset(); }; ///< set the structure to defaults value delta_sig_block_size(const delta_sig_block_size & ref) = default; delta_sig_block_size(delta_sig_block_size && ref) noexcept = default; delta_sig_block_size & operator = (const delta_sig_block_size & ref) = default; delta_sig_block_size & operator = (delta_sig_block_size && ref) noexcept = default; ~delta_sig_block_size() = default; bool operator == (const delta_sig_block_size & ref) const; /// reset to default value void reset(); /// whether structure has default values bool equals_default() { return (*this) == delta_sig_block_size(); }; /// check the sanity of the provided values void check() const; /// calculate the value of the block size given the file size /// \param[in] filesize is the size of the file which delta signature to be calculated /// \return the block len to use for delta signature U_I calculate(const infinint & filesize) const; }; } // end of namespace #endif dar-2.7.15/src/libdar/terminateur.hpp0000644000175000017500000000500714636066467014415 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file terminateur.hpp /// \brief the terminateur class which defines the position of the catalogue /// \ingroup Private /// /// the terminateur is a byte sequence present as the last bytes of an archive /// which indicates how much byte backward libdar must skip back to find the /// beginning of the catalogue. #ifndef TERMINATEUR_HPP #define TERMINATEUR_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" #include "archive_version.hpp" namespace libdar { /// \addtogroup Private /// @{ /// terminateur class indicates the location of the beginning of the catalogue /// it is the last bytes sequence of an archive. class terminateur { public : terminateur() {}; // fields "pos" and "t_start" are objects initialized by their default constructor terminateur(const terminateur & ref) = default; terminateur(terminateur && ref) noexcept = default; terminateur & operator = (const terminateur & ref) = default; terminateur & operator = (terminateur && ref) noexcept = default; ~terminateur() = default; void set_catalogue_start(infinint xpos) { pos = xpos; }; void dump(generic_file &f); void read_catalogue(generic_file &f, bool with_elastic, const archive_version & reading_ver, const infinint & where_from = 0); infinint get_catalogue_start() const { return pos; }; infinint get_terminateur_start() const { return t_start; }; private : infinint pos; infinint t_start; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/archive_options.cpp0000644000175000017500000016725514636067146015256 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include #include "archive_options.hpp" #include "entrepot_local.hpp" #include "tools.hpp" #include "hash_fichier.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { // general default values static const U_32 default_crypto_size = 10240; static const path default_ref_chem("/"); static const U_I default_min_compr_size = 100; static const comparison_fields default_comparison_fields = comparison_fields::all; static const crit_constant_action default_crit_action = crit_constant_action(data_preserve, EA_preserve); static const string default_user_comment = "N/A"; static const U_32 default_delta_sig_min_size = 10240; static const infinint default_iteration_count = 200000; static const infinint default_iteration_count_argon2 = 10000; // some local helper functions inline void archive_option_destroy_mask(mask * & ptr) noexcept; inline void archive_option_clean_mask(mask * & ptr, bool all = true); inline void archive_option_destroy_crit_action(crit_action * & ptr) noexcept; inline void archive_option_clean_crit_action(crit_action * & ptr); ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// inline void archive_option_clean_mask(mask * & ptr, bool all) { archive_option_destroy_mask(ptr); ptr = new (nothrow) bool_mask(all); if(ptr == nullptr) throw Ememory("archive_option_clean_mask"); } inline void archive_option_destroy_mask(mask * & ptr) noexcept { if(ptr != nullptr) { delete ptr; ptr = nullptr; } } inline void archive_option_clean_crit_action(crit_action * & ptr) { archive_option_destroy_crit_action(ptr); ptr = default_crit_action.clone(); if(ptr == nullptr) throw Ememory("archive_options::archive_option_clean_crit_action"); } inline void archive_option_destroy_crit_action(crit_action * & ptr) noexcept { if(ptr != nullptr) { delete ptr; ptr = nullptr; } } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// archive_options_read::archive_options_read(archive_options_read && ref) noexcept : x_ref_chem("/") { move_from(std::move(ref)); } archive_options_read::archive_options_read() : x_ref_chem(default_ref_chem) { clear(); } void archive_options_read::clear() { // setting the default values for all options x_crypto = crypto_algo::none; x_pass.clear(); x_crypto_size = default_crypto_size; x_input_pipe = ""; x_output_pipe = ""; x_execute = ""; x_info_details = false; x_lax = false; x_sequential_read = false; x_slice_min_digits = 0; x_entrepot = shared_ptr(new (nothrow) entrepot_local("", "", false)); // never using furtive_mode to read slices if(!x_entrepot) throw Ememory("archive_options_read::clear"); x_ignore_signature_check_failure = false; x_multi_threaded_crypto = 1; x_multi_threaded_compress = 1; // external_cat = false; x_ref_chem = default_ref_chem; x_ref_basename = ""; x_ref_crypto = crypto_algo::none; x_ref_pass.clear(); x_ref_crypto_size = default_crypto_size; x_ref_execute = ""; x_ref_slice_min_digits = 0; x_ref_entrepot = shared_ptr(new (nothrow) entrepot_local("", "", false)); // never using furtive_mode to read slices if(!x_ref_entrepot) throw Ememory("archive_options_read::clear"); x_header_only = false; } void archive_options_read::set_default_crypto_size() { x_crypto_size = default_crypto_size; x_ref_crypto_size = default_crypto_size; } void archive_options_read::unset_external_catalogue() { x_ref_chem = default_ref_chem; x_ref_basename = ""; external_cat = false; } const path & archive_options_read::get_ref_path() const { NLS_SWAP_IN; try { if(!external_cat) throw Elibcall("archive_options_read::get_external_catalogue", gettext("Cannot get catalogue of reference as it has not been provided")); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return x_ref_chem; } const string & archive_options_read::get_ref_basename() const { NLS_SWAP_IN; try { if(!external_cat) throw Elibcall("archive_options_read::get_external_catalogue", gettext("Error, catalogue of reference has not been provided")); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; return x_ref_basename; } void archive_options_read::copy_from(const archive_options_read & ref) { x_crypto = ref.x_crypto; x_pass = ref.x_pass; x_crypto_size = ref.x_crypto_size; x_input_pipe = ref.x_input_pipe; x_output_pipe = ref.x_output_pipe; x_execute = ref.x_execute; x_info_details = ref.x_info_details; x_lax = ref.x_lax; x_sequential_read = ref.x_sequential_read; x_slice_min_digits = ref.x_slice_min_digits; if(!ref.x_entrepot) throw SRC_BUG; x_entrepot = ref.x_entrepot; x_ignore_signature_check_failure = ref.x_ignore_signature_check_failure; x_multi_threaded_crypto = ref.x_multi_threaded_crypto; x_multi_threaded_compress = ref.x_multi_threaded_compress; // external_cat = ref.external_cat; x_ref_chem = ref.x_ref_chem; x_ref_basename = ref.x_ref_basename; x_ref_crypto = ref.x_ref_crypto; x_ref_pass = ref.x_ref_pass; x_ref_crypto_size = ref.x_ref_crypto_size; x_ref_execute = ref.x_ref_execute; x_ref_slice_min_digits = ref.x_ref_slice_min_digits; if(!ref.x_ref_entrepot) throw SRC_BUG; x_ref_entrepot = ref.x_ref_entrepot; x_header_only = ref.x_header_only; } void archive_options_read::move_from(archive_options_read && ref) noexcept { x_crypto = move(ref.x_crypto); x_pass = move(ref.x_pass); x_crypto_size = move(ref.x_crypto_size); x_input_pipe = move(ref.x_input_pipe); x_output_pipe = move(ref.x_output_pipe); x_execute = move(ref.x_execute); x_info_details = move(ref.x_info_details); x_lax = move(ref.x_lax); x_sequential_read = move(ref.x_sequential_read); x_slice_min_digits = move(ref.x_slice_min_digits); x_entrepot = move(ref.x_entrepot); x_ignore_signature_check_failure = move(ref.x_ignore_signature_check_failure); x_multi_threaded_crypto = move(ref.x_multi_threaded_crypto); x_multi_threaded_compress = move(ref.x_multi_threaded_compress); external_cat = move(ref.external_cat); x_ref_chem = move(ref.x_ref_chem); x_ref_basename = move(ref.x_ref_basename); x_ref_crypto = move(ref.x_ref_crypto); x_ref_pass = move(ref.x_ref_pass); x_ref_crypto_size = move(ref.x_ref_crypto_size); x_ref_execute = move(ref.x_ref_execute); x_ref_slice_min_digits = move(ref.x_ref_slice_min_digits); x_ref_entrepot = move(ref.x_ref_entrepot); x_header_only = move(ref.x_header_only); } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// archive_options_create::archive_options_create() { nullifyptr(); try { clear(); } catch(...) { destroy(); throw; } } archive_options_create::archive_options_create(const archive_options_create & ref) { nullifyptr(); try { copy_from(ref); } catch(...) { destroy(); throw; } } void archive_options_create::clear() { NLS_SWAP_IN; try { destroy(); archive_option_clean_mask(x_selection); archive_option_clean_mask(x_subtree); archive_option_clean_mask(x_ea_mask); archive_option_clean_mask(x_compr_mask); archive_option_clean_mask(x_backup_hook_file_mask, false); archive_option_clean_mask(x_delta_mask); x_ref_arch.reset(); x_allow_over = true; x_warn_over = true; x_info_details = false; x_display_treated = false; x_display_treated_only_dir = false; x_display_skipped = false; x_display_finished = false; x_pause = 0; x_empty_dir = false; x_compr_algo = compression::none; x_compression_level = 9; x_compression_block_size = 0; x_file_size = 0; x_first_file_size = 0; x_execute = ""; x_crypto = crypto_algo::none; x_pass.clear(); x_crypto_size = default_crypto_size; x_gnupg_recipients.clear(); x_gnupg_signatories.clear(); x_min_compr_size = default_min_compr_size; x_nodump = false; exclude_by_ea = ""; x_what_to_check = default_comparison_fields; x_hourshift = 0; x_empty = false; x_alter_atime = true; x_old_alter_atime = true; #if FURTIVE_READ_MODE_AVAILABLE x_furtive_read = true; #else x_furtive_read = false; #endif x_same_fs = false; x_same_fs_include.clear(); x_same_fs_exclude.clear(); x_snapshot = false; x_cache_directory_tagging = false; x_fixed_date = 0; x_slice_permission = ""; x_slice_user_ownership = ""; x_slice_group_ownership = ""; x_repeat_count = 3; x_repeat_byte = 1; x_sequential_marks = true; x_sparse_file_min_size = 15; // min value to activate the feature (0 means no detection of sparse_file) x_security_check = true; x_user_comment = default_user_comment; x_hash = hash_algo::none; x_slice_min_digits = 0; x_backup_hook_file_execute = ""; x_ignore_unknown = false; x_entrepot = shared_ptr(new (nothrow) entrepot_local( "", "", false)); // never using furtive_mode to read slices if(!x_entrepot) throw Ememory("archive_options_create::clear"); x_scope = all_fsa_families(); x_multi_threaded_crypto = 1; x_multi_threaded_compress = 1; x_delta_diff = true; x_delta_signature = false; has_delta_mask_been_set = false; x_delta_sig_min_size = default_delta_sig_min_size; x_auto_zeroing_neg_dates = false; x_ignored_as_symlink.clear(); x_modified_data_detection = modified_data_detection::mtime_size; if(compile_time::libargon2()) { x_iteration_count = default_iteration_count_argon2; x_kdf_hash = hash_algo::argon2; } else { x_kdf_hash = hash_algo::sha1; x_iteration_count = default_iteration_count; } x_sig_block_len.reset(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_create::set_selection(const mask & selection) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_selection); x_selection = selection.clone(); if(x_selection == nullptr) throw Ememory("archive_options_create::set_selection"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_create::set_subtree(const mask & subtree) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_subtree); x_subtree = subtree.clone(); if(x_subtree == nullptr) throw Ememory("archive_options_create::set_subtree"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_create::set_ea_mask(const mask & ea_mask) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_ea_mask); x_ea_mask = ea_mask.clone(); if(x_ea_mask == nullptr) throw Ememory("archive_options_create::set_ea_mask"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_create::set_compr_mask(const mask & compr_mask) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_compr_mask); x_compr_mask = compr_mask.clone(); if(x_compr_mask == nullptr) throw Ememory("archive_options_create::set_compr_mask"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_create::set_furtive_read_mode(bool furtive_read) { NLS_SWAP_IN; try { #if FURTIVE_READ_MODE_AVAILABLE x_furtive_read = furtive_read; if(furtive_read) { x_old_alter_atime = x_alter_atime; x_alter_atime = true; // this is required to avoid libdar manipulating ctime of inodes } else x_alter_atime = x_old_alter_atime; #else if(furtive_read) throw Ecompilation(gettext("Furtive read mode")); x_furtive_read = false; #endif } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_create::set_backup_hook(const std::string & execute, const mask & which_files) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_backup_hook_file_mask); x_backup_hook_file_mask = which_files.clone(); if(x_backup_hook_file_mask == nullptr) throw Ememory("archive_options_create::set_backup_hook"); x_backup_hook_file_execute = execute; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_create::set_delta_mask(const mask & delta_mask) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_delta_mask); x_delta_mask = delta_mask.clone(); if(x_delta_mask == nullptr) throw Ememory("archive_options_create::set_delta_mask"); has_delta_mask_been_set = true; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_create::set_hash_algo(hash_algo hash) { if(hash == hash_algo::argon2) throw Erange("archive_options_create", gettext("argon2 hash algorithm is only used for key derivation function, it is not adapted to file or slice hashing")); x_hash = hash; } void archive_options_create::nullifyptr() noexcept { x_selection = x_subtree = x_ea_mask = x_compr_mask = x_backup_hook_file_mask = x_delta_mask = nullptr; } void archive_options_create::destroy() noexcept { archive_option_destroy_mask(x_selection); archive_option_destroy_mask(x_subtree); archive_option_destroy_mask(x_ea_mask); archive_option_destroy_mask(x_compr_mask); archive_option_destroy_mask(x_backup_hook_file_mask); archive_option_destroy_mask(x_delta_mask); } void archive_options_create::copy_from(const archive_options_create & ref) { x_selection = nullptr; x_subtree = nullptr; x_ea_mask = nullptr; x_compr_mask = nullptr; x_backup_hook_file_mask = nullptr; x_delta_mask = nullptr; if(ref.x_selection == nullptr) throw SRC_BUG; if(ref.x_subtree == nullptr) throw SRC_BUG; if(ref.x_ea_mask == nullptr) throw SRC_BUG; if(ref.x_compr_mask == nullptr) throw SRC_BUG; if(ref.x_backup_hook_file_mask == nullptr) throw SRC_BUG; x_selection = ref.x_selection->clone(); x_subtree = ref.x_subtree->clone(); x_ea_mask = ref.x_ea_mask->clone(); x_compr_mask = ref.x_compr_mask->clone(); x_backup_hook_file_mask = ref.x_backup_hook_file_mask->clone(); if(x_selection == nullptr || x_subtree == nullptr || x_ea_mask == nullptr || x_compr_mask == nullptr || x_backup_hook_file_mask == nullptr) throw Ememory("archive_options_create::copy_from"); x_ref_arch = ref.x_ref_arch; x_allow_over = ref.x_allow_over; x_warn_over = ref.x_warn_over; x_info_details = ref.x_info_details; x_display_treated = ref.x_display_treated; x_display_treated_only_dir = ref.x_display_treated_only_dir; x_display_skipped = ref.x_display_skipped; x_display_finished = ref.x_display_finished; x_pause = ref.x_pause; x_empty_dir = ref.x_empty_dir; x_compr_algo = ref.x_compr_algo; x_compression_level = ref.x_compression_level; x_compression_block_size = ref.x_compression_block_size; x_file_size = ref.x_file_size; x_first_file_size = ref.x_first_file_size; x_execute = ref.x_execute; x_crypto = ref.x_crypto; x_pass = ref.x_pass; x_crypto_size = ref.x_crypto_size; x_gnupg_recipients = ref.x_gnupg_recipients; x_gnupg_signatories = ref.x_gnupg_signatories; x_min_compr_size = ref.x_min_compr_size; x_nodump = ref.x_nodump; x_what_to_check = ref.x_what_to_check; x_hourshift = ref.x_hourshift; x_empty = ref.x_empty; x_alter_atime = ref.x_alter_atime; x_old_alter_atime = ref.x_old_alter_atime; x_furtive_read = ref.x_furtive_read; x_same_fs = ref.x_same_fs; x_same_fs_include = ref.x_same_fs_include; x_same_fs_exclude = ref.x_same_fs_exclude; x_snapshot = ref.x_snapshot; x_cache_directory_tagging = ref.x_cache_directory_tagging; x_fixed_date = ref.x_fixed_date; x_slice_permission = ref.x_slice_permission; x_slice_user_ownership = ref.x_slice_user_ownership; x_slice_group_ownership = ref.x_slice_group_ownership; x_repeat_count = ref.x_repeat_count; x_repeat_byte = ref.x_repeat_byte; x_sequential_marks = ref.x_sequential_marks; x_sparse_file_min_size = ref.x_sparse_file_min_size; x_security_check = ref.x_security_check; x_user_comment = ref.x_user_comment; x_hash = ref.x_hash; x_slice_min_digits = ref.x_slice_min_digits; x_backup_hook_file_execute = ref.x_backup_hook_file_execute; x_ignore_unknown = ref.x_ignore_unknown; if(!ref.x_entrepot) throw SRC_BUG; x_entrepot = ref.x_entrepot; if(!x_entrepot) throw Ememory("archive_options_create::copy_from"); x_scope = ref.x_scope; x_multi_threaded_crypto = ref.x_multi_threaded_crypto; x_multi_threaded_compress = ref.x_multi_threaded_compress; x_delta_diff = ref.x_delta_diff; x_delta_signature = ref.x_delta_signature; x_delta_mask = ref.x_delta_mask->clone(); has_delta_mask_been_set = ref.has_delta_mask_been_set; x_delta_sig_min_size = ref.x_delta_sig_min_size; x_auto_zeroing_neg_dates = ref.x_auto_zeroing_neg_dates; x_ignored_as_symlink = ref.x_ignored_as_symlink; x_modified_data_detection = ref.x_modified_data_detection; x_iteration_count = ref.x_iteration_count; x_kdf_hash = ref.x_kdf_hash; x_sig_block_len = ref.x_sig_block_len; } void archive_options_create::move_from(archive_options_create && ref) noexcept { swap(x_selection, ref.x_selection); swap(x_subtree, ref.x_subtree); swap(x_ea_mask, ref.x_ea_mask); swap(x_compr_mask, ref.x_compr_mask); swap(x_backup_hook_file_mask, ref.x_backup_hook_file_mask); x_entrepot = ref.x_entrepot; x_ref_arch = move(ref.x_ref_arch); x_allow_over = move(ref.x_allow_over); x_warn_over = move(ref.x_warn_over); x_info_details = move(ref.x_info_details); x_display_treated = move(ref.x_display_treated); x_display_treated_only_dir = move(ref.x_display_treated_only_dir); x_display_skipped = move(ref.x_display_skipped); x_display_finished = move(ref.x_display_finished); x_pause = move(ref.x_pause); x_empty_dir = move(ref.x_empty_dir); x_compr_algo = move(ref.x_compr_algo); x_compression_level = move(ref.x_compression_level); x_compression_block_size = move(ref.x_compression_block_size); x_file_size = move(ref.x_file_size); x_first_file_size = move(ref.x_first_file_size); x_execute = move(ref.x_execute); x_crypto = move(ref.x_crypto); x_pass = move(ref.x_pass); x_crypto_size = move(ref.x_crypto_size); x_gnupg_recipients = move(ref.x_gnupg_recipients); x_gnupg_signatories = move(ref.x_gnupg_signatories); x_min_compr_size = move(ref.x_min_compr_size); x_nodump = move(ref.x_nodump); x_what_to_check = move(ref.x_what_to_check); x_hourshift = move(ref.x_hourshift); x_empty = move(ref.x_empty); x_alter_atime = move(ref.x_alter_atime); x_old_alter_atime = move(ref.x_old_alter_atime); x_furtive_read = move(ref.x_furtive_read); x_same_fs = move(ref.x_same_fs); x_same_fs_include = move(ref.x_same_fs_include); x_same_fs_exclude = move(ref.x_same_fs_exclude); x_snapshot = move(ref.x_snapshot); x_cache_directory_tagging = move(ref.x_cache_directory_tagging); x_fixed_date = move(ref.x_fixed_date); x_slice_permission = move(ref.x_slice_permission); x_slice_user_ownership = move(ref.x_slice_user_ownership); x_slice_group_ownership = move(ref.x_slice_group_ownership); x_repeat_count = move(ref.x_repeat_count); x_repeat_byte = move(ref.x_repeat_byte); x_sequential_marks = move(ref.x_sequential_marks); x_sparse_file_min_size = move(ref.x_sparse_file_min_size); x_security_check = move(ref.x_security_check); x_user_comment = move(ref.x_user_comment); x_hash = move(ref.x_hash); x_slice_min_digits = move(ref.x_slice_min_digits); x_backup_hook_file_execute = move(ref.x_backup_hook_file_execute); x_ignore_unknown = move(ref.x_ignore_unknown); x_scope = move(ref.x_scope); x_multi_threaded_crypto = move(ref.x_multi_threaded_crypto); x_multi_threaded_compress = move(ref.x_multi_threaded_compress); x_delta_diff = move(ref.x_delta_diff); x_delta_signature = move(ref.x_delta_signature); x_delta_mask = move(ref.x_delta_mask->clone()); has_delta_mask_been_set = move(ref.has_delta_mask_been_set); x_delta_sig_min_size = move(ref.x_delta_sig_min_size); x_auto_zeroing_neg_dates = move(ref.x_auto_zeroing_neg_dates); x_ignored_as_symlink = move(ref.x_ignored_as_symlink); x_modified_data_detection = move(ref.x_modified_data_detection); x_iteration_count = move(ref.x_iteration_count); x_kdf_hash = move(ref.x_kdf_hash); x_sig_block_len = move(ref.x_sig_block_len); } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// archive_options_isolate::archive_options_isolate() { nullifyptr(); try { clear(); } catch(...) { destroy(); throw; } } archive_options_isolate::archive_options_isolate(const archive_options_isolate & ref) { nullifyptr(); try { copy_from(ref); } catch(...) { destroy(); throw; } } void archive_options_isolate::clear() { NLS_SWAP_IN; try { destroy(); x_allow_over = true; x_warn_over = true; x_info_details = false; x_pause = 0; x_algo = compression::none; x_compression_level = 9; x_compression_block_size = 0; x_file_size = 0; x_first_file_size = 0; x_execute = ""; x_crypto = crypto_algo::none; x_pass.clear(); x_crypto_size = default_crypto_size; x_gnupg_recipients.clear(); x_gnupg_signatories.clear(); x_empty = false; x_slice_permission = ""; x_slice_user_ownership = ""; x_slice_group_ownership = ""; x_user_comment = default_user_comment; x_hash = hash_algo::none; x_slice_min_digits = 0; x_sequential_marks = true; x_entrepot = shared_ptr(new (nothrow) entrepot_local("", "", false)); // never using furtive_mode to read slices if(!x_entrepot) throw Ememory("archive_options_isolate::clear"); x_multi_threaded_crypto = 1; x_multi_threaded_compress = 1; x_delta_signature = false; archive_option_clean_mask(x_delta_mask); has_delta_mask_been_set = false; x_delta_sig_min_size = default_delta_sig_min_size; if(compile_time::libargon2()) { x_iteration_count = default_iteration_count_argon2; x_kdf_hash = hash_algo::argon2; } else { x_kdf_hash = hash_algo::sha1; x_iteration_count = default_iteration_count; } x_sig_block_len.reset(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_isolate::set_delta_mask(const mask & delta_mask) { NLS_SWAP_IN; try { if(!compile_time::librsync()) throw Ecompilation("librsync"); else { archive_option_destroy_mask(x_delta_mask); x_delta_mask = delta_mask.clone(); if(x_delta_mask == nullptr) throw Ememory("archive_options_create::set_delta_mask"); has_delta_mask_been_set = true; } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_isolate::set_hash_algo(hash_algo hash) { if(hash == hash_algo::argon2) throw Erange("archive_options_isolate", gettext("argon2 hash algorithm is only used for key derivation function, it is not adapted to file or slice hashing")); x_hash = hash; } void archive_options_isolate::destroy() noexcept { archive_option_destroy_mask(x_delta_mask); } void archive_options_isolate::copy_from(const archive_options_isolate & ref) { x_allow_over = ref.x_allow_over; x_warn_over = ref.x_warn_over; x_info_details = ref.x_info_details; x_pause = ref.x_pause; x_algo = ref.x_algo; x_compression_level = ref.x_compression_level; x_compression_block_size = ref.x_compression_block_size; x_file_size = ref.x_file_size; x_first_file_size = ref.x_first_file_size; x_execute = ref.x_execute; x_crypto = ref.x_crypto; x_pass = ref.x_pass; x_crypto_size = ref.x_crypto_size; x_gnupg_recipients = ref.x_gnupg_recipients; x_gnupg_signatories = ref.x_gnupg_signatories; x_empty = ref.x_empty; x_slice_permission = ref.x_slice_permission; x_slice_user_ownership = ref.x_slice_user_ownership; x_slice_group_ownership = ref.x_slice_group_ownership; x_user_comment = ref.x_user_comment; x_hash = ref.x_hash; x_slice_min_digits = ref.x_slice_min_digits; x_sequential_marks = ref.x_sequential_marks; if(ref.x_entrepot == nullptr) throw SRC_BUG; x_entrepot = ref.x_entrepot; if(x_entrepot == nullptr) throw Ememory("archive_options_isolate::copy_from"); x_multi_threaded_crypto = ref.x_multi_threaded_crypto; x_multi_threaded_compress = ref.x_multi_threaded_compress; x_delta_signature = ref.x_delta_signature; x_delta_mask = ref.x_delta_mask->clone(); has_delta_mask_been_set = ref.has_delta_mask_been_set; x_delta_sig_min_size = ref.x_delta_sig_min_size; x_iteration_count = ref.x_iteration_count; x_kdf_hash = ref.x_kdf_hash; } void archive_options_isolate::move_from(archive_options_isolate && ref) noexcept { swap(x_entrepot, ref.x_entrepot); swap(x_delta_mask, ref.x_delta_mask); x_allow_over = move(ref.x_allow_over); x_warn_over = move(ref.x_warn_over); x_info_details = move(ref.x_info_details); x_pause = move(ref.x_pause); x_algo = move(ref.x_algo); x_compression_level = move(ref.x_compression_level); x_compression_block_size = move(ref.x_compression_block_size); x_file_size = move(ref.x_file_size); x_first_file_size = move(ref.x_first_file_size); x_execute = move(ref.x_execute); x_crypto = move(ref.x_crypto); x_pass = move(ref.x_pass); x_crypto_size = move(ref.x_crypto_size); x_gnupg_recipients = move(ref.x_gnupg_recipients); x_gnupg_signatories = move(ref.x_gnupg_signatories); x_empty = move(ref.x_empty); x_slice_permission = move(ref.x_slice_permission); x_slice_user_ownership = move(ref.x_slice_user_ownership); x_slice_group_ownership = move(ref.x_slice_group_ownership); x_user_comment = move(ref.x_user_comment); x_hash = move(ref.x_hash); x_slice_min_digits = move(ref.x_slice_min_digits); x_sequential_marks = move(ref.x_sequential_marks); x_multi_threaded_crypto = move(ref.x_multi_threaded_crypto); x_multi_threaded_compress = move(ref.x_multi_threaded_compress); x_delta_signature = move(ref.x_delta_signature); has_delta_mask_been_set = move(ref.has_delta_mask_been_set); x_delta_sig_min_size = move(ref.x_delta_sig_min_size); x_iteration_count = move(ref.x_iteration_count); x_kdf_hash = move(ref.x_kdf_hash); } void archive_options_isolate::nullifyptr() noexcept { x_entrepot = nullptr; x_delta_mask = nullptr; } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// void archive_options_merge::clear() { NLS_SWAP_IN; try { destroy(); archive_option_clean_mask(x_selection); archive_option_clean_mask(x_subtree); archive_option_clean_mask(x_ea_mask); archive_option_clean_mask(x_compr_mask); archive_option_clean_mask(x_delta_mask); archive_option_clean_crit_action(x_overwrite); x_ref.reset(); x_allow_over = true; x_warn_over = true; x_info_details = false; x_display_treated = false; x_display_treated_only_dir = false; x_display_skipped = false; x_pause = 0; x_empty_dir = false; x_compr_algo = compression::none; x_compression_level = 9; x_compression_block_size = 0; x_file_size = 0; x_first_file_size = 0; x_execute = ""; x_crypto = crypto_algo::none; x_pass.clear(); x_crypto_size = default_crypto_size; x_gnupg_recipients.clear(); x_gnupg_signatories.clear(); x_min_compr_size = default_min_compr_size; x_empty = false; x_keep_compressed = false; x_slice_permission = ""; x_slice_user_ownership = ""; x_slice_group_ownership = ""; x_decremental = false; x_sequential_marks = true; x_sparse_file_min_size = 0; // disabled by default x_user_comment = default_user_comment; x_hash = hash_algo::none; x_slice_min_digits = 0; x_entrepot = shared_ptr(new (nothrow) entrepot_local("", "", false)); // never using furtive_mode to read slices if(x_entrepot == nullptr) throw Ememory("archive_options_merge::clear"); x_scope = all_fsa_families(); x_multi_threaded_crypto = 1; x_multi_threaded_compress = 1; x_delta_signature = true; has_delta_mask_been_set = false; x_delta_sig_min_size = default_delta_sig_min_size; if(compile_time::libargon2()) { x_iteration_count = default_iteration_count_argon2; x_kdf_hash = hash_algo::argon2; } else { x_kdf_hash = hash_algo::sha1; x_iteration_count = default_iteration_count; } x_sig_block_len.reset(); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_merge::set_selection(const mask & selection) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_selection); x_selection = selection.clone(); if(x_selection == nullptr) throw Ememory("archive_options_merge::set_selection"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_merge::set_subtree(const mask & subtree) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_subtree); x_subtree = subtree.clone(); if(x_subtree == nullptr) throw Ememory("archive_options_merge::set_subtree"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_merge::set_overwriting_rules(const crit_action & overwrite) { NLS_SWAP_IN; try { archive_option_destroy_crit_action(x_overwrite); x_overwrite = overwrite.clone(); if(x_overwrite == nullptr) throw Ememory("archive_options_merge::set_overwriting_rules"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_merge::set_ea_mask(const mask & ea_mask) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_ea_mask); x_ea_mask = ea_mask.clone(); if(x_ea_mask == nullptr) throw Ememory("archive_options_merge::set_ea_mask"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_merge::set_compr_mask(const mask & compr_mask) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_compr_mask); x_compr_mask = compr_mask.clone(); if(x_compr_mask == nullptr) throw Ememory("archive_options_merge::set_compr_mask"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_merge::set_delta_mask(const mask & delta_mask) { NLS_SWAP_IN; try { if(!compile_time::librsync()) throw Ecompilation("librsync"); else { archive_option_destroy_mask(x_delta_mask); x_delta_mask = delta_mask.clone(); if(x_delta_mask == nullptr) throw Ememory("archive_options_create::set_delta_mask"); has_delta_mask_been_set = true; } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_merge::set_hash_algo(hash_algo hash) { if(hash == hash_algo::argon2) throw Erange("archive_options_merge", gettext("argon2 hash algorithm is only used for key derivation function, it is not adapted to file or slice hashing")); x_hash = hash; } void archive_options_merge::destroy() noexcept { archive_option_destroy_mask(x_selection); archive_option_destroy_mask(x_subtree); archive_option_destroy_mask(x_ea_mask); archive_option_destroy_mask(x_compr_mask); archive_option_destroy_mask(x_delta_mask); archive_option_destroy_crit_action(x_overwrite); } void archive_options_merge::copy_from(const archive_options_merge & ref) { nullifyptr(); try { if(ref.x_selection == nullptr) throw SRC_BUG; if(ref.x_subtree == nullptr) throw SRC_BUG; if(ref.x_ea_mask == nullptr) throw SRC_BUG; if(ref.x_compr_mask == nullptr) throw SRC_BUG; if(ref.x_overwrite == nullptr) throw SRC_BUG; if(ref.x_entrepot == nullptr) throw SRC_BUG; if(ref.x_delta_mask == nullptr) throw SRC_BUG; x_selection = ref.x_selection->clone(); x_subtree = ref.x_subtree->clone(); x_ea_mask = ref.x_ea_mask->clone(); x_compr_mask = ref.x_compr_mask->clone(); x_overwrite = ref.x_overwrite->clone(); x_entrepot = ref.x_entrepot; x_delta_mask = ref.x_delta_mask->clone(); if(x_selection == nullptr || x_subtree == nullptr || x_ea_mask == nullptr || x_compr_mask == nullptr || x_overwrite == nullptr || x_entrepot == nullptr || x_delta_mask == nullptr) throw Ememory("archive_options_merge::copy_from"); x_ref = ref.x_ref; x_allow_over = ref.x_allow_over; x_warn_over = ref.x_warn_over; x_info_details = ref.x_info_details; x_display_treated = ref.x_display_treated; x_display_treated_only_dir = ref.x_display_treated_only_dir; x_display_skipped = ref.x_display_skipped; x_pause = ref.x_pause; x_empty_dir = ref.x_empty_dir; x_compr_algo = ref.x_compr_algo; x_compression_level = ref.x_compression_level; x_compression_block_size = ref.x_compression_block_size; x_file_size = ref.x_file_size; x_first_file_size = ref.x_first_file_size; x_execute = ref.x_execute; x_crypto = ref.x_crypto; x_pass = ref.x_pass; x_crypto_size = ref.x_crypto_size; x_gnupg_recipients = ref.x_gnupg_recipients; x_gnupg_signatories = ref.x_gnupg_signatories; x_min_compr_size = ref.x_min_compr_size; x_empty = ref.x_empty; x_keep_compressed = ref.x_keep_compressed; x_slice_permission = ref.x_slice_permission; x_slice_user_ownership = ref.x_slice_user_ownership; x_slice_group_ownership = ref.x_slice_group_ownership; x_decremental = ref.x_decremental; x_sequential_marks = ref.x_sequential_marks; x_sparse_file_min_size = ref.x_sparse_file_min_size; x_user_comment = ref.x_user_comment; x_hash = ref.x_hash; x_slice_min_digits = ref.x_slice_min_digits; x_scope = ref.x_scope; x_multi_threaded_crypto = ref.x_multi_threaded_crypto; x_multi_threaded_compress = ref.x_multi_threaded_compress; x_delta_signature = ref.x_delta_signature; has_delta_mask_been_set = ref.has_delta_mask_been_set; x_delta_sig_min_size = ref.x_delta_sig_min_size; x_iteration_count = ref.x_iteration_count; x_kdf_hash = ref.x_kdf_hash; x_sig_block_len = ref.x_sig_block_len; } catch(...) { clear(); throw; } } void archive_options_merge::move_from(archive_options_merge && ref) noexcept { swap(x_selection, ref.x_selection); swap(x_subtree, ref.x_subtree); swap(x_ea_mask,ref.x_ea_mask); swap(x_compr_mask, ref.x_compr_mask); swap(x_overwrite, ref.x_overwrite); swap(x_entrepot, ref.x_entrepot); swap(x_delta_mask, ref.x_delta_mask); x_ref = move(ref.x_ref); x_allow_over = move(ref.x_allow_over); x_warn_over = move(ref.x_warn_over); x_info_details = move(ref.x_info_details); x_display_treated = move(ref.x_display_treated); x_display_treated_only_dir = move(ref.x_display_treated_only_dir); x_display_skipped = move(ref.x_display_skipped); x_pause = move(ref.x_pause); x_empty_dir = move(ref.x_empty_dir); x_compr_algo = move(ref.x_compr_algo); x_compression_level = move(ref.x_compression_level); x_compression_block_size = move(ref.x_compression_block_size); x_file_size = move(ref.x_file_size); x_first_file_size = move(ref.x_first_file_size); x_execute = move(ref.x_execute); x_crypto = move(ref.x_crypto); x_pass = move(ref.x_pass); x_crypto_size = move(ref.x_crypto_size); x_gnupg_recipients = move(ref.x_gnupg_recipients); x_gnupg_signatories = move(ref.x_gnupg_signatories); x_min_compr_size = move(ref.x_min_compr_size); x_empty = move(ref.x_empty); x_keep_compressed = move(ref.x_keep_compressed); x_slice_permission = move(ref.x_slice_permission); x_slice_user_ownership = move(ref.x_slice_user_ownership); x_slice_group_ownership = move(ref.x_slice_group_ownership); x_decremental = move(ref.x_decremental); x_sequential_marks = move(ref.x_sequential_marks); x_sparse_file_min_size = move(ref.x_sparse_file_min_size); x_user_comment = move(ref.x_user_comment); x_hash = move(ref.x_hash); x_slice_min_digits = move(ref.x_slice_min_digits); x_scope = move(ref.x_scope); x_multi_threaded_crypto = move(ref.x_multi_threaded_crypto); x_multi_threaded_compress = move(ref.x_multi_threaded_compress); x_delta_signature = move(ref.x_delta_signature); has_delta_mask_been_set = move(ref.has_delta_mask_been_set); x_delta_sig_min_size = move(ref.x_delta_sig_min_size); x_iteration_count = move(ref.x_iteration_count); x_kdf_hash = move(ref.x_kdf_hash); x_sig_block_len = move(ref.x_sig_block_len); } void archive_options_merge::nullifyptr() noexcept { x_selection = x_subtree = x_ea_mask = x_compr_mask = x_delta_mask = nullptr; x_overwrite = nullptr; x_entrepot = nullptr; } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// void archive_options_extract::clear() { NLS_SWAP_IN; try { archive_option_clean_mask(x_selection); archive_option_clean_mask(x_subtree); archive_option_clean_mask(x_ea_mask); archive_option_clean_crit_action(x_overwrite); x_warn_over = true; x_info_details = false; x_display_treated = false; x_display_treated_only_dir = false; x_display_skipped = false; x_flat = false; x_what_to_check = default_comparison_fields; x_warn_remove_no_match = true; x_empty = false; x_empty_dir = true; x_dirty = dirty_warn; x_only_deleted = false; x_ignore_deleted = false; x_scope = all_fsa_families(); x_ignore_unix_sockets = false; x_in_place = false; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_extract::set_selection(const mask & selection) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_selection); x_selection = selection.clone(); if(x_selection == nullptr) throw Ememory("archive_options_extract::set_selection"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_extract::set_subtree(const mask & subtree) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_subtree); x_subtree = subtree.clone(); if(x_subtree == nullptr) throw Ememory("archive_options_extract::set_subtree"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_extract::set_ea_mask(const mask & ea_mask) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_ea_mask); x_ea_mask = ea_mask.clone(); if(x_ea_mask == nullptr) throw Ememory("archive_options_extract::set_ea_mask"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_extract::set_overwriting_rules(const crit_action & over) { NLS_SWAP_IN; try { archive_option_destroy_crit_action(x_overwrite); x_overwrite = over.clone(); if(x_overwrite == nullptr) throw Ememory("archive_options_extract::set_overwriting_rules"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_extract::destroy() noexcept { archive_option_destroy_mask(x_selection); archive_option_destroy_mask(x_subtree); archive_option_destroy_mask(x_ea_mask); archive_option_destroy_crit_action(x_overwrite); } void archive_options_extract::nullifyptr() noexcept { x_selection = x_subtree = x_ea_mask = nullptr; x_overwrite = nullptr; } void archive_options_extract::copy_from(const archive_options_extract & ref) { nullifyptr(); try { if(ref.x_selection == nullptr) throw SRC_BUG; if(ref.x_subtree == nullptr) throw SRC_BUG; if(ref.x_ea_mask == nullptr) throw SRC_BUG; if(ref.x_overwrite == nullptr) throw SRC_BUG; x_selection = ref.x_selection->clone(); x_subtree = ref.x_subtree->clone(); x_ea_mask = ref.x_ea_mask->clone(); x_overwrite = ref.x_overwrite->clone(); if(x_selection == nullptr || x_subtree == nullptr || x_ea_mask == nullptr || x_overwrite == nullptr) throw Ememory("archive_options_extract::copy_from"); x_warn_over = ref.x_warn_over; x_info_details = ref.x_info_details; x_display_treated = ref.x_display_treated; x_display_treated_only_dir = ref.x_display_treated_only_dir; x_display_skipped = ref.x_display_skipped; x_flat = ref.x_flat; x_what_to_check = ref.x_what_to_check; x_warn_remove_no_match = ref.x_warn_remove_no_match; x_empty = ref.x_empty; x_empty_dir = ref.x_empty_dir; x_dirty = ref.x_dirty; x_only_deleted = ref.x_only_deleted; x_ignore_deleted = ref.x_ignore_deleted; x_scope = ref.x_scope; x_ignore_unix_sockets = ref.x_ignore_unix_sockets; x_in_place = ref.x_in_place; } catch(...) { clear(); throw; } } void archive_options_extract::move_from(archive_options_extract && ref) noexcept { swap(x_selection, ref.x_selection); swap(x_subtree, ref.x_subtree); swap(x_ea_mask, ref.x_ea_mask); swap(x_overwrite, ref.x_overwrite); x_warn_over = move(ref.x_warn_over); x_info_details = move(ref.x_info_details); x_display_treated = move(ref.x_display_treated); x_display_treated_only_dir = move(ref.x_display_treated_only_dir); x_display_skipped = move(ref.x_display_skipped); x_flat = move(ref.x_flat); x_what_to_check = move(ref.x_what_to_check); x_warn_remove_no_match = move(ref.x_warn_remove_no_match); x_empty = move(ref.x_empty); x_empty_dir = move(ref.x_empty_dir); x_dirty = move(ref.x_dirty); x_only_deleted = move(ref.x_only_deleted); x_ignore_deleted = move(ref.x_ignore_deleted); x_scope = move(ref.x_scope); x_ignore_unix_sockets = move(ref.x_ignore_unix_sockets); x_in_place = move(ref.x_in_place); } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// void archive_options_listing::clear() { NLS_SWAP_IN; try { destroy(); x_info_details = false; archive_option_clean_mask(x_selection); archive_option_clean_mask(x_subtree); x_filter_unsaved = false; x_slicing_location = false; if(x_slicing_first != nullptr) { delete x_slicing_first; x_slicing_first = nullptr; } if(x_slicing_others != nullptr) { delete x_slicing_others; x_slicing_others = nullptr; } x_display_ea = false; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_listing::set_selection(const mask & selection) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_selection); x_selection = selection.clone(); if(x_selection == nullptr) throw Ememory("archive_options_listing::set_selection"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_listing::set_subtree(const mask & subtree) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_subtree); x_subtree = subtree.clone(); if(x_subtree == nullptr) throw Ememory("archive_options_listing::set_subtree"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_listing::set_user_slicing(const infinint & slicing_first, const infinint & slicing_others) { if(x_slicing_first == nullptr) { x_slicing_first = new (nothrow) infinint(slicing_first); if(x_slicing_first == nullptr) throw Ememory("archive_options_listing::set_user_slicing"); } else *x_slicing_first = slicing_first; if(x_slicing_others == nullptr) { x_slicing_others = new (nothrow) infinint(slicing_others); if(x_slicing_others == nullptr) throw Ememory("archive_options_listing::set_user_slicing"); } else *x_slicing_others = slicing_others; } const mask & archive_options_listing::get_selection() const { if(x_selection == nullptr) throw Erange("archive_option_listing", dar_gettext("No mask available")); return *x_selection; } const mask & archive_options_listing::get_subtree() const { if(x_subtree == nullptr) throw Erange("archive_option_listing", dar_gettext("No mask available")); return *x_subtree; } bool archive_options_listing::get_user_slicing(infinint & slicing_first, infinint & slicing_others) const { if(x_slicing_first != nullptr && x_slicing_others != nullptr) { slicing_first = *x_slicing_first; slicing_others = *x_slicing_others; return true; } else return false; } void archive_options_listing::destroy() noexcept { archive_option_destroy_mask(x_selection); archive_option_destroy_mask(x_subtree); if(x_slicing_first != nullptr) { delete x_slicing_first; x_slicing_first = nullptr; } if(x_slicing_others != nullptr) { delete x_slicing_others; x_slicing_others = nullptr; } } void archive_options_listing::nullifyptr() noexcept { x_selection = x_subtree = nullptr; x_slicing_first = x_slicing_others = nullptr; } void archive_options_listing::copy_from(const archive_options_listing & ref) { nullifyptr(); try { if(ref.x_selection == nullptr) throw SRC_BUG; x_selection = ref.x_selection->clone(); if(ref.x_subtree == nullptr) throw SRC_BUG; x_subtree = ref.x_subtree->clone(); if(x_selection == nullptr || x_subtree == nullptr) throw Ememory("archive_options_listing::copy_from"); if(ref.x_slicing_first != nullptr) { x_slicing_first = new (nothrow) infinint(*ref.x_slicing_first); if(x_slicing_first == nullptr) throw Ememory("archive_options_listing::copy_from"); } if(ref.x_slicing_others != nullptr) { x_slicing_others = new (nothrow) infinint(*ref.x_slicing_others); if(x_slicing_others == nullptr) throw Ememory("archive_options_listing::copy_from"); } x_info_details = ref.x_info_details; x_filter_unsaved = ref.x_filter_unsaved; x_slicing_location = ref.x_slicing_location; x_display_ea = ref.x_display_ea; } catch(...) { clear(); throw; } } void archive_options_listing::move_from(archive_options_listing && ref) noexcept { swap(x_selection, ref.x_selection); swap(x_subtree, ref.x_subtree); swap(x_slicing_first, ref.x_slicing_first); swap(x_slicing_others, ref.x_slicing_others); x_info_details = move(ref.x_info_details); x_filter_unsaved = move(ref.x_filter_unsaved); x_slicing_location = move(ref.x_slicing_location); x_display_ea = move(ref.x_display_ea); } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// void archive_options_diff::clear() { NLS_SWAP_IN; try { destroy(); archive_option_clean_mask(x_selection); archive_option_clean_mask(x_subtree); x_info_details = false; x_display_treated = false; x_display_treated_only_dir = false; x_display_skipped = false; archive_option_clean_mask(x_ea_mask); x_what_to_check = comparison_fields::all; x_alter_atime = true; x_old_alter_atime = true; #if FURTIVE_READ_MODE_AVAILABLE x_furtive_read = true; #else x_furtive_read = false; #endif x_hourshift = 0; x_compare_symlink_date = true; x_scope = all_fsa_families(); x_in_place = false; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_diff::set_selection(const mask & selection) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_selection); x_selection = selection.clone(); if(x_selection == nullptr) throw Ememory("archive_options_diff::set_selection"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_diff::set_subtree(const mask & subtree) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_subtree); x_subtree = subtree.clone(); if(x_subtree == nullptr) throw Ememory("archive_options_diff::set_subtree"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_diff::set_ea_mask(const mask & ea_mask) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_ea_mask); x_ea_mask = ea_mask.clone(); if(x_ea_mask == nullptr) throw Ememory("archive_options_dif::set_ea_mask"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_diff::set_furtive_read_mode(bool furtive_read) { NLS_SWAP_IN; try { #if FURTIVE_READ_MODE_AVAILABLE x_furtive_read = furtive_read; if(furtive_read) { x_old_alter_atime = x_alter_atime; x_alter_atime = true; // this is required to avoid libdar manipulating ctime of inodes } else x_alter_atime = x_old_alter_atime; #else if(furtive_read) throw Ecompilation(gettext("Furtive read mode")); x_furtive_read = false; #endif } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_diff::destroy() noexcept { archive_option_destroy_mask(x_selection); archive_option_destroy_mask(x_subtree); archive_option_destroy_mask(x_ea_mask); } void archive_options_diff::nullifyptr() noexcept { x_selection = x_subtree = x_ea_mask = nullptr; } void archive_options_diff::copy_from(const archive_options_diff & ref) { nullifyptr(); try { if(ref.x_selection == nullptr) throw SRC_BUG; if(ref.x_subtree == nullptr) throw SRC_BUG; if(ref.x_ea_mask == nullptr) throw SRC_BUG; x_selection = ref.x_selection->clone(); x_subtree = ref.x_subtree->clone(); x_ea_mask = ref.x_ea_mask->clone(); if(x_selection == nullptr || x_subtree == nullptr || x_ea_mask == nullptr) throw Ememory("archive_options_extract::copy_from"); x_info_details = ref.x_info_details; x_display_treated = ref.x_display_treated; x_display_treated_only_dir = ref.x_display_treated_only_dir; x_display_skipped = ref.x_display_skipped; x_what_to_check = ref.x_what_to_check; x_alter_atime = ref.x_alter_atime; x_old_alter_atime = ref.x_old_alter_atime; x_furtive_read = ref.x_furtive_read; x_hourshift = ref.x_hourshift; x_compare_symlink_date = ref.x_compare_symlink_date; x_scope = ref.x_scope; x_in_place = ref.x_in_place; } catch(...) { clear(); throw; } } void archive_options_diff::move_from(archive_options_diff && ref) noexcept { swap(x_selection, ref.x_selection); swap(x_subtree, ref.x_subtree); swap(x_ea_mask, ref.x_ea_mask); x_info_details = move(ref.x_info_details); x_display_treated = move(ref.x_display_treated); x_display_treated_only_dir = move(ref.x_display_treated_only_dir); x_display_skipped = move(ref.x_display_skipped); x_what_to_check = move(ref.x_what_to_check); x_alter_atime = move(ref.x_alter_atime); x_old_alter_atime = move(ref.x_old_alter_atime); x_furtive_read = move(ref.x_furtive_read); x_hourshift = move(ref.x_hourshift); x_compare_symlink_date = move(ref.x_compare_symlink_date); x_scope = move(ref.x_scope); x_in_place = move(ref.x_in_place); } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// void archive_options_test::clear() { NLS_SWAP_IN; try { destroy(); archive_option_clean_mask(x_selection); archive_option_clean_mask(x_subtree); x_info_details = false; x_display_treated = false; x_display_treated_only_dir = false; x_display_skipped = false; x_empty = false; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_test::set_selection(const mask & selection) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_selection); x_selection = selection.clone(); if(x_selection == nullptr) throw Ememory("archive_options_test::set_selection"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_test::set_subtree(const mask & subtree) { NLS_SWAP_IN; try { archive_option_destroy_mask(x_subtree); x_subtree = subtree.clone(); if(x_subtree == nullptr) throw Ememory("archive_option_test::set_subtree"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_test::destroy() noexcept { archive_option_destroy_mask(x_selection); archive_option_destroy_mask(x_subtree); } void archive_options_test::nullifyptr() noexcept { x_selection = x_subtree = nullptr; } void archive_options_test::copy_from(const archive_options_test & ref) { x_selection = nullptr; x_subtree = nullptr; try { if(ref.x_selection == nullptr) throw SRC_BUG; if(ref.x_subtree == nullptr) throw SRC_BUG; x_selection = ref.x_selection->clone(); x_subtree = ref.x_subtree->clone(); if(x_selection == nullptr || x_subtree == nullptr) throw Ememory("archive_options_extract::copy_from"); x_info_details = ref.x_info_details; x_display_treated = ref.x_display_treated; x_display_treated_only_dir = ref.x_display_treated_only_dir; x_display_skipped = ref.x_display_skipped; x_empty = ref.x_empty; } catch(...) { clear(); throw; } } void archive_options_test::move_from(archive_options_test && ref) noexcept { swap(x_selection, ref.x_selection); swap(x_subtree, ref.x_subtree); x_info_details = move(ref.x_info_details); x_display_treated = move(ref.x_display_treated); x_display_treated_only_dir = move(ref.x_display_treated_only_dir); x_display_skipped = move(ref.x_display_skipped); x_empty = move(ref.x_empty); } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// archive_options_repair::archive_options_repair() { nullifyptr(); clear(); } archive_options_repair::archive_options_repair(const archive_options_repair & ref) { nullifyptr(); copy_from(ref); } void archive_options_repair::clear() { NLS_SWAP_IN; try { x_allow_over = true; x_warn_over = true; x_info_details = false; x_display_treated = false; x_display_treated_only_dir = false; x_display_skipped = false; x_display_finished = false; x_pause = 0; x_file_size = 0; x_first_file_size = 0; x_execute = ""; x_crypto = crypto_algo::none; x_pass.clear(); x_crypto_size = default_crypto_size; x_gnupg_recipients.clear(); x_gnupg_signatories.clear(); x_empty = false; x_slice_permission = ""; x_slice_user_ownership = ""; x_slice_group_ownership = ""; x_user_comment = default_user_comment; x_hash = hash_algo::none; x_slice_min_digits = 0; x_entrepot = shared_ptr(new (nothrow) entrepot_local( "", "", false)); // never using furtive_mode to read slices if(x_entrepot == nullptr) throw Ememory("archive_options_repair::clear"); x_multi_threaded_crypto = 1; x_multi_threaded_compress = 1; } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void archive_options_repair::set_hash_algo(hash_algo hash) { if(hash == hash_algo::argon2) throw Erange("archive_options_repair", gettext("argon2 hash algorithm is only used for key derivation function, it is not adapted to file or slice hashing")); x_hash = hash; } void archive_options_repair::copy_from(const archive_options_repair & ref) { x_allow_over = ref.x_allow_over; x_warn_over = ref.x_warn_over; x_info_details = ref.x_info_details; x_display_treated = ref.x_display_treated; x_display_treated_only_dir = ref.x_display_treated_only_dir; x_display_skipped = ref.x_display_skipped; x_display_finished = ref.x_display_finished; x_pause = ref.x_pause; x_file_size = ref.x_file_size; x_first_file_size = ref.x_first_file_size; x_execute = ref.x_execute; x_crypto = ref.x_crypto; x_pass = ref.x_pass; x_crypto_size = ref.x_crypto_size; x_gnupg_recipients = ref.x_gnupg_recipients; x_gnupg_signatories = ref.x_gnupg_signatories; x_empty = ref.x_empty; x_slice_permission = ref.x_slice_permission; x_slice_user_ownership = ref.x_slice_user_ownership; x_slice_group_ownership = ref.x_slice_group_ownership; x_user_comment = ref.x_user_comment; x_hash = ref.x_hash; x_slice_min_digits = ref.x_slice_min_digits; x_entrepot = ref.x_entrepot; x_multi_threaded_crypto = ref.x_multi_threaded_crypto; x_multi_threaded_compress = ref.x_multi_threaded_compress; } void archive_options_repair::move_from(archive_options_repair && ref) noexcept { x_entrepot = ref.x_entrepot; x_allow_over = move(ref.x_allow_over); x_warn_over = move(ref.x_warn_over); x_info_details = move(ref.x_info_details); x_display_treated = move(ref.x_display_treated); x_display_treated_only_dir = move(ref.x_display_treated_only_dir); x_display_skipped = move(ref.x_display_skipped); x_display_finished = move(ref.x_display_finished); x_pause = move(ref.x_pause); x_file_size = move(ref.x_file_size); x_first_file_size = move(ref.x_first_file_size); x_execute = move(ref.x_execute); x_crypto = move(ref.x_crypto); x_pass = move(ref.x_pass); x_crypto_size = move(ref.x_crypto_size); x_gnupg_recipients = move(ref.x_gnupg_recipients); x_gnupg_signatories = move(ref.x_gnupg_signatories); x_empty = move(ref.x_empty); x_slice_permission = move(ref.x_slice_permission); x_slice_user_ownership = move(ref.x_slice_user_ownership); x_slice_group_ownership = move(ref.x_slice_group_ownership); x_user_comment = move(ref.x_user_comment); x_hash = move(ref.x_hash); x_slice_min_digits = move(ref.x_slice_min_digits); x_multi_threaded_crypto = move(ref.x_multi_threaded_crypto); x_multi_threaded_compress = move(ref.x_multi_threaded_compress); } } // end of namespace dar-2.7.15/src/libdar/database_aux.hpp0000644000175000017500000000553714636066467014507 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file database_aux.hpp /// \brief set of datastructures used to interact with a database object /// \ingroup API #ifndef DATABASE_AUX_HPP #define DATABASE_AUX_HPP #include "../my_config.h" namespace libdar { /// \addtogroup API /// @{ /// the available status of a lookup enum class db_lookup { found_present, ///< file data/EA has been found completely usable found_removed, ///< file data/EA has been found as removed at that date not_found, ///< no such file has been found in any archive of the base not_restorable ///< file data/EA has been found existing at that date but not possible to restore (lack of data, missing archive in base, etc.) }; /// the status for a founded entry /// \note when restoring an et_patch entry, it is required to also restore all to all intermediate /// et_patch since the last et_saved status. While restoring et_inode /// only need to restore the previous et_saved and the latest et_inode available. Intermediate et_inode /// have not to be restored. enum class db_etat { et_saved, ///< data/EA present in the archive et_patch, ///< data present as patch since the previous version et_patch_unusable, ///< data present as patch but base version not found in archive set et_inode, ///< only inode metadata has been modified since previous version et_present, ///< file/EA present in the archive but data not saved (differential backup) et_removed, ///< file/EA stored as deleted since archive of reference or file/EA not present in the archive et_absent ///< file not even mentionned in the archive, This entry is equivalent to et_removed, but is required to be able to properly re-order the archive when user asks to do so. The dates associated to this state are computed from neighbor archives in the database }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/erreurs_ext.hpp0000644000175000017500000000500214636066467014420 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file erreurs_ext.hpp /// \brief contains some additional exception class thrown by libdar /// \ingroup Private #ifndef ERREURS_EXT_HPP #define ERREURS_EXT_HPP #include "erreurs.hpp" #include "infinint.hpp" namespace libdar { /// \addtogroup Private /// @{ /// Ethread_cancel with infinint attribute class Ethread_cancel_with_attr : public Ethread_cancel { public : Ethread_cancel_with_attr(bool now, U_64 x_flag, const infinint & attr); Ethread_cancel_with_attr(const Ethread_cancel_with_attr & ref): Ethread_cancel(ref) { copy_from(ref); }; Ethread_cancel_with_attr(Ethread_cancel_with_attr && ref) noexcept : Ethread_cancel(std::move(ref)) { x_attr = nullptr; std::swap(x_attr, ref.x_attr); }; Ethread_cancel_with_attr & operator = (const Ethread_cancel_with_attr & ref) { detruit(); copy_from(ref); return *this; }; Ethread_cancel_with_attr & operator = (Ethread_cancel_with_attr && ref) noexcept; ~Ethread_cancel_with_attr() { detruit(); }; const infinint get_attr() const { return *x_attr; }; private : // infinint may throw Ebug exception from destructor. // Having a field of type infinint lead this class // to have a default destructor throwing Ebug too // which implies Egeneric to have the same which // makes circular dependency as Ebug cannot be defined // before Egeneric. Here is the reason why we use a infinint* here infinint *x_attr; void detruit(); void copy_from(const Ethread_cancel_with_attr & ref); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/limitint.hpp0000644000175000017500000005463014636066467013715 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file limitint.hpp /// \brief the reviewed implementation of infinint based on system limited integers /// \ingroup API /// /// the limitint template class implementation defined in this module can /// handle positive integers and detect overflow. It shares with infinint the same /// interface, so it can be use in place of it, but throw Elimitint exceptions if /// overflow is detected. #ifndef LIMITINT_HPP #define LIMITINT_HPP #include "../my_config.h" extern "C" { #if HAVE_SYS_TYPES_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif } // end extern "C" #include #include "integers.hpp" #include "erreurs.hpp" #include "int_tools.hpp" #include "proto_generic_file.hpp" #define ZEROED_SIZE 50 namespace libdar { /// \addtogroup API /// @{ /// limitint template class /// \ingroup Private /// /// the limitint template class implementation can /// handle positive integers and detect overflow. It shares with infinint the same /// interface, so it can be use in place of it, but throw Elimitint exceptions if /// overflow is detected. /// this template class receive as argument a positive integer atomic type /// In particular it is assumed that the sizeof() operator gives the amount of /// byte of information that this type can handle, and it is also assumed that /// the bytes of information are contiguous. /// \note if you just want to convert an infinint /// integer to its decimal representation see the /// class libdar::deci template class limitint { public : #if SIZEOF_OFF_T > SIZEOF_TIME_T #if SIZEOF_OFF_T > SIZEOF_SIZE_T limitint(off_t a = 0) { limitint_from(a); }; #else limitint(size_t a = 0) { limitint_from(a); }; #endif #else #if SIZEOF_TIME_T > SIZEOF_SIZE_T limitint(time_t a = 0) { limitint_from(a); }; #else limitint(size_t a = 0) { limitint_from(a); }; #endif #endif // read an limitint from a file limitint(proto_generic_file & x); limitint(const limitint & ref) = default; limitint(limitint && ref) noexcept = default; limitint & operator = (const limitint & ref) = default; limitint & operator = (limitint && ref) noexcept = default; // for coherent footprint with real infinint ~limitint() = default; void dump(proto_generic_file &x) const; // write byte sequence to file void read(proto_generic_file &f) { build_from_file(f); }; limitint & operator += (const limitint & ref); limitint & operator -= (const limitint & ref); limitint & operator *= (const limitint & ref); template limitint power(const T & exponent) const; limitint & operator /= (const limitint & ref); limitint & operator %= (const limitint & ref); limitint & operator &= (const limitint & ref); limitint & operator |= (const limitint & ref); limitint & operator ^= (const limitint & ref); limitint & operator >>= (U_32 bit); limitint & operator >>= (limitint bit); limitint & operator <<= (U_32 bit); limitint & operator <<= (limitint bit); limitint operator ++(int a) { limitint ret = *this; ++(*this); return ret; }; limitint operator --(int a) { limitint ret = *this; --(*this); return ret; }; limitint & operator ++() { return *this += 1; }; limitint & operator --() { return *this -= 1; }; U_32 operator % (U_32 arg) const; // increment the argument up to a legal value for its storage type and decrement the object in consequence // note that the initial value of the argument is not ignored ! // when the object is null the value of the argument stays the same as before template void unstack(T &v) { limitint_unstack_to(v); } limitint get_storage_size() const; // it returns number of byte of information necessary to store the integer unsigned char operator [] (const limitint & position) const; // return in little endian order the information bytes storing the integer bool is_zero() const { return field == 0; }; bool operator < (const limitint &x) const { return field < x.field; }; bool operator == (const limitint &x) const { return field == x.field; }; bool operator > (const limitint &x) const { return field > x.field; }; bool operator <= (const limitint &x) const { return field <= x.field; }; bool operator != (const limitint &x) const { return field != x.field; }; bool operator >= (const limitint &x) const { return field >= x.field; }; static bool is_system_big_endian(); B debug_get_max() const { return max_value; }; B debug_get_bytesize() const { return bytesize; }; B debug_get_field() const { return field; }; private : B field; void build_from_file(proto_generic_file & x); template void limitint_from(T a); template T max_val_of(T x); template void limitint_unstack_to(T &a); ///////////////////////// // static statments // static const int TG = 4; static const U_32 sizeof_field = sizeof(B); // number of bytes enum endian { big_endian, little_endian, not_initialized }; using group = unsigned char[TG]; static endian used_endian; static const U_I bytesize = sizeof(B); static const B max_value = ~B(0) > 0 ? ~B(0) : ~(B(1) << (bytesize*8 - 1)); static U_8 zeroed_field[ZEROED_SIZE]; static void setup_endian(); }; template U_8 limitint::zeroed_field[ZEROED_SIZE]; template limitint operator + (const limitint &, const limitint &); template inline limitint operator + (const limitint & a, U_I b) { return a + limitint(b); } template limitint operator - (const limitint &, const limitint &); template inline limitint operator - (const limitint & a, U_I b) { return a - limitint(b); } template limitint operator * (const limitint &, const limitint &); template inline limitint operator * (const limitint & a, U_I b) { return a * limitint(b); } template limitint operator / (const limitint &, const limitint &); template limitint operator / (const limitint & a, U_I b) { return a / limitint(b); } template limitint operator % (const limitint &, const limitint &); template limitint operator >> (const limitint & a, U_32 bit); template limitint operator >> (const limitint & a, const limitint & bit); template limitint operator << (const limitint & a, U_32 bit); template limitint operator << (const limitint & a, const limitint & bit); template limitint operator & (const limitint & a, U_32 bit); template limitint operator & (const limitint & a, const limitint & bit); template limitint operator | (const limitint & a, U_32 bit); template limitint operator | (const limitint & a, const limitint & bit); template limitint operator ^ (const limitint & a, U_32 bit); template limitint operator ^ (const limitint & a, const limitint & bit); template inline void euclide(T a, T b, T & q, T &r) { q = a/b; r = a%b; } template inline void euclide(limitint a, U_I b, limitint & q, limitint &r) { euclide(a, limitint(b), q, r); } #ifndef INFININT_BASE_TYPE #error INFININT_BASE_TYPE not defined cannot instantiate template #else using infinint = limitint; #endif } // end of namespace /////////////////////////////////////////////////////////////////////// ///////// template implementation //////////////////////////////////// /////////////////////////////////////////////////////////////////////// namespace libdar { template typename limitint::endian limitint::used_endian = not_initialized; template limitint::limitint(proto_generic_file & x) { build_from_file(x); } template void limitint::build_from_file(proto_generic_file & x) { unsigned char a; bool fin = false; limitint skip = 0; char *ptr = (char *)&field; S_I lu; int_tools_bitfield bf; while(!fin) { lu = x.read((char *)&a, 1); if(lu <= 0) throw Erange("limitint::build_from_file(proto_generic_file)", gettext("Reached end of file before all data could be read")); if(a == 0) ++skip; else // end of size field { // computing the size to read U_I pos = 0; int_tools_expand_byte(a, bf); for(S_I i = 0; i < 8; ++i) pos += bf[i]; if(pos != 1) throw Erange("limitint::build_from_file(proto_generic_file)", gettext("Badly formed \"infinint\" or not supported format")); // more than 1 bit is set to 1 pos = 0; while(bf[pos] == 0) ++pos; pos += 1; // bf starts at zero, but bit zero means 1 TG of length skip *= 8; skip += pos; skip *= TG; if(skip.field > bytesize) throw Elimitint(); field = 0; // important to also clear "unread" bytes by this call lu = x.read(ptr, skip.field); if(used_endian == not_initialized) setup_endian(); if(used_endian == little_endian) int_tools_swap_bytes((unsigned char *)ptr, skip.field); else field >>= (bytesize - skip.field)*8; fin = true; } } } template void limitint::dump(proto_generic_file & x) const { B width = bytesize; B pos; unsigned char last_width; B justification; S_I direction = +1; unsigned char *ptr, *fin; if(used_endian == not_initialized) setup_endian(); if(used_endian == little_endian) { direction = -1; ptr = (unsigned char *)(&field) + (bytesize - 1); fin = (unsigned char *)(&field) - 1; } else { direction = +1; ptr = (unsigned char *)(&field); fin = (unsigned char *)(&field) + bytesize; } while(ptr != fin && *ptr == 0) { ptr += direction; --width; } if(width == 0) width = 1; // minimum size of information is 1 byte // "width" is the informational field size in byte // TG is the width in TG, thus the number of bit that must have // the preamble euclide(width, (const B)(TG), width, justification); if(justification != 0) // in case we need to add some bytes to have a width multiple of TG ++width; // we need then one more group to have a width multiple of TG euclide(width, (const B)(8), width, pos); if(pos == 0) { width--; // division is exact, only last bit of the preambule is set last_width = 0x80 >> 7; // as we add the last byte separately width gets shorter by 1 byte } else // division non exact, the last_width (last byte), make the rounding { U_16 pos_s = (U_16)(0xFFFF & pos); last_width = 0x80 >> (pos_s - 1); } // now we write the preamble except the last byte. All these are zeros. while(width != 0) if(width > ZEROED_SIZE) { x.write((char *)zeroed_field, ZEROED_SIZE); width -= ZEROED_SIZE; } else { x.write((char *)zeroed_field, width); width = 0; } // now we write the last byte of the preambule, which has only one bit set x.write((char *)&last_width, 1); // we need now to write some justification byte to have an informational field multiple of TG if(justification != 0) { justification = TG - justification; if(justification > ZEROED_SIZE) throw SRC_BUG; else x.write((char *)zeroed_field, justification); } // now we continue dumping the informational bytes: if(ptr == fin) // field is equal to zero x.write((char *)zeroed_field, 1); else // we have some bytes to write down while(ptr != fin) { x.write((char *)ptr, 1); ptr += direction; } } template limitint & limitint::operator += (const limitint & arg) { B res = field + arg.field; if(res < field || res < arg.field) throw Elimitint(); else field = res; return *this; } template limitint & limitint::operator -= (const limitint & arg) { if(field < arg.field) throw Erange("limitint::operator", gettext("Subtracting an \"infinint\" greater than the first, \"infinint\" cannot be negative")); // now processing the operation field -= arg.field; return *this; } template limitint & limitint::operator *= (const limitint & arg) { static const B max_power = bytesize*8 - 1; B total = int_tools_higher_power_of_2(field) + int_tools_higher_power_of_2(arg.field) + 1; // for an explaination about "+2" see NOTES if(total > max_power) // this is a bit too much restrictive, but unless remaking bit by bit, the operation, // I don't see how to simply (and fast) know the result has not overflowed. // of course, it would be fast and easy to access the CPU flag register to check for overflow, // but that would not be portable, and unfortunately I haven't found any standart C++ expression that // could transparently access to it. throw Elimitint(); total = field*arg.field; if(field != 0 && arg.field != 0) if(total < field || total < arg.field) throw Elimitint(); field = total; return *this; } template template limitint limitint::power(const T & exponent) const { limitint ret = 1; for(T count = 0; count < exponent; ++count) ret *= *this; return ret; } template limitint & limitint::operator /= (const limitint & arg) { if(arg == 0) throw Einfinint("limitint.cpp : operator /=", gettext("Division by zero")); field /= arg.field; return *this; } template limitint & limitint::operator %= (const limitint & arg) { if(arg == 0) throw Einfinint("limitint.cpp : operator %=", gettext("Division by zero")); field %= arg.field; return *this; } template limitint & limitint::operator >>= (U_32 bit) { if(bit >= sizeof_field*8) field = 0; else field >>= bit; return *this; } template limitint & limitint::operator >>= (limitint bit) { field >>= bit.field; return *this; } template limitint & limitint::operator <<= (U_32 bit) { if(bit + int_tools_higher_power_of_2(field) >= bytesize*8) throw Elimitint(); field <<= bit; return *this; } template limitint & limitint::operator <<= (limitint bit) { if(bit.field + int_tools_higher_power_of_2(field) >= bytesize*8) throw Elimitint(); field <<= bit.field; return *this; } template limitint & limitint::operator &= (const limitint & arg) { field &= arg.field; return *this; } template limitint & limitint::operator |= (const limitint & arg) { field |= arg.field; return *this; } template limitint & limitint::operator ^= (const limitint & arg) { field ^= arg.field; return *this; } template U_32 limitint::operator % (U_32 arg) const { return U_32(field % arg); } template template void limitint::limitint_from(T a) { if(sizeof(a) <= bytesize || a <= (T)(max_value)) field = B(a); else throw Elimitint(); } template template T limitint::max_val_of(T x) { x = 0; x = ~x; if(x < 1) // T is a signed integer type, we are not comparing to zero to avoid compiler warning when the template is used against unsigned integers { x = 1; x = int_tools_rotate_right_one_bit(x); x = ~x; } return x; } template template void limitint::limitint_unstack_to(T &a) { // T is supposed to be an unsigned "integer" // (ie.: sizeof returns the width of the storage bit field and no sign bit is present) // Note : static here avoids the recalculation of max_T at each call static const T max_T = max_val_of(a); T step = max_T - a; if(field < (B)(step) && (T)(field) < step) { a += field; field = 0; } else { field -= step; a = max_T; } } template limitint limitint::get_storage_size() const { B tmp = field; B ret = 0; while(tmp != 0) { tmp >>= 8; ret++; } return limitint(ret); } template unsigned char limitint::operator [] (const limitint & position) const { B tmp = field; B index = position.field; // C++ has only class protection, not object protection while(index > 0) { tmp >>= 8; index--; } return (unsigned char)(tmp & 0xFF); } template void limitint::setup_endian() { if(integers_system_is_big_endian()) used_endian = big_endian; else used_endian = little_endian; (void)memset(zeroed_field, 0, ZEROED_SIZE); } template bool limitint::is_system_big_endian() { if(used_endian == not_initialized) setup_endian(); switch(used_endian) { case big_endian: return true; case little_endian: return false; case not_initialized: throw SRC_BUG; default: throw SRC_BUG; } } /////////////////////////////////////////////////////////////////////// ///////////////// friends and not friends of limitint ///////////////// /////////////////////////////////////////////////////////////////////// template limitint operator + (const limitint & a, const limitint & b) { limitint ret = a; ret += b; return ret; } template limitint operator - (const limitint & a, const limitint & b) { limitint ret = a; ret -= b; return ret; } template limitint operator * (const limitint & a, const limitint & b) { limitint ret = a; ret *= b; return ret; } template limitint operator / (const limitint & a, const limitint & b) { limitint ret = a; ret /= b; return ret; } template limitint operator % (const limitint & a, const limitint & b) { limitint ret = a; ret %= b; return ret; } template limitint operator >> (const limitint & a, U_32 bit) { limitint ret = a; ret >>= bit; return ret; } template limitint operator >> (const limitint & a, const limitint & bit) { limitint ret = a; ret >>= bit; return ret; } template limitint operator << (const limitint & a, U_32 bit) { limitint ret = a; ret <<= bit; return ret; } template limitint operator << (const limitint & a, const limitint & bit) { limitint ret = a; ret <<= bit; return ret; } template limitint operator & (const limitint & a, U_32 bit) { limitint ret = a; ret &= bit; return ret; } template limitint operator & (const limitint & a, const limitint & bit) { limitint ret = a; ret &= bit; return ret; } template limitint operator | (const limitint & a, U_32 bit) { limitint ret = a; ret |= bit; return ret; } template limitint operator | (const limitint & a, const limitint & bit) { limitint ret = a; ret |= bit; return ret; } template limitint operator ^ (const limitint & a, U_32 bit) { limitint ret = a; ret ^= bit; return ret; } template limitint operator ^ (const limitint & a, const limitint & bit) { limitint ret = a; ret ^= bit; return ret; } /// @} } // end of namespace #endif dar-2.7.15/src/libdar/header_version.hpp0000644000175000017500000001513214636066467015053 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file header_version.hpp /// \brief archive global header/trailer structure is defined here /// \ingroup Private #ifndef HEADER_VERSION_HPP #define HEADER_VERSION_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" #include "archive_version.hpp" #include "crypto.hpp" #include "slice_layout.hpp" #include "compression.hpp" #include "user_interaction.hpp" #include "memory_file.hpp" #include "archive_aux.hpp" namespace libdar { /// \addtogroup Private /// @{ /// manages the archive header and trailer class header_version { public: header_version(); header_version(const header_version & ref) { copy_from(ref); }; header_version(header_version && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; header_version & operator = (const header_version & ref) { detruit(); copy_from(ref); return * this; }; header_version & operator = (header_version && ref) noexcept { move_from(std::move(ref)); return *this; }; ~header_version() { detruit(); }; /// read the header or trailer from the archive void read(generic_file &f, user_interaction & dialog, bool lax_mode); /// write down the object to the archive (as header if wrote at the beginning of the archive, as trailer is at the end) void write(generic_file &f) const; // settings void set_edition(const archive_version & ed) { edition = ed; }; void set_compression_algo(const compression & zip) { algo_zip = zip; }; void set_command_line(const std::string & line) { cmd_line = line; }; void set_initial_offset(const infinint & offset) { initial_offset = offset; }; void set_sym_crypto_algo(const crypto_algo & algo) { sym = algo; }; /// the object pointed to by key passes to the responsibility of this header_version object void set_crypted_key(memory_file *key) { if(key == nullptr) throw SRC_BUG; clear_crypted_key(); crypted_key = key; }; void clear_crypted_key() { if(crypted_key != nullptr) { delete crypted_key; crypted_key = nullptr; } }; /// the object pointed to by layout is passed under the responsibility of this header_version object void set_slice_layout(slice_layout *layout) { if(layout == nullptr) throw SRC_BUG; clear_slice_layout(); ref_layout = layout; }; void clear_slice_layout() { if(ref_layout != nullptr) { delete ref_layout; ref_layout = nullptr; } }; void set_tape_marks(bool presence) { has_tape_marks = presence; }; void set_signed(bool is_signed) { arch_signed = is_signed; }; void set_salt(const std::string & arg) { salt = arg; has_kdf_params = true; }; void set_iteration_count(const infinint & arg) { iteration_count = arg; has_kdf_params = true; }; void set_kdf_hash(hash_algo algo); void set_compression_block_size(const infinint & bs) { compr_bs = bs; }; // gettings const archive_version & get_edition() const { return edition; }; compression get_compression_algo() const { return algo_zip; }; const std::string & get_command_line() const { return cmd_line; }; const infinint & get_initial_offset() const { return initial_offset; }; bool is_ciphered() const { return ciphered || sym != crypto_algo::none; }; bool is_signed() const { return arch_signed; }; crypto_algo get_sym_crypto_algo() const { return sym; }; std::string get_sym_crypto_name() const; std::string get_asym_crypto_name() const; memory_file *get_crypted_key() const { return crypted_key; }; const slice_layout *get_slice_layout() const { return ref_layout; }; bool get_tape_marks() const { return has_tape_marks; }; const std::string & get_salt() const { return salt; }; const infinint & get_iteration_count() const { return iteration_count; }; hash_algo get_kdf_hash() const { return kdf_hash; }; const infinint & get_compression_block_size() const { return compr_bs; }; // display void display(user_interaction & dialg) const; // clear void clear(); private: archive_version edition; ///< archive format compression algo_zip; ///< compression algorithm used std::string cmd_line; ///< used long ago to store cmd_line, then abandonned, then recycled as a user comment field infinint initial_offset; ///< defines at which offset starts the archive (passed the archive header), this field is obiously only used in the trailer not in the header // has to be set to zero when it is unknown, in that case this field is not dump to archive crypto_algo sym; ///< strong encryption algorithm used for symmetrical encryption memory_file *crypted_key;///< optional field containing the asymmetrically ciphered key used for strong encryption ciphering slice_layout *ref_layout;///< optional field used in isolated catalogues to record the slicing layout of their archive of reference bool has_tape_marks; ///< whether the archive contains tape marks aka escape marks aka sequence marks bool ciphered; ///< whether the archive is ciphered, even if we do not know its crypto algorithm (old archives) bool arch_signed; ///< whether the archive is signed bool has_kdf_params; ///< has salt/ineration/kdf_hash fields valid std::string salt; ///< used for key derivation infinint iteration_count;///< used for key derivation hash_algo kdf_hash; ///< used for key derivation infinint compr_bs; ///< the compression block size (0 for legacy compression mode) void nullifyptr() noexcept { crypted_key = nullptr; ref_layout = nullptr; }; void copy_from(const header_version & ref); void move_from(header_version && ref) noexcept; void detruit(); static constexpr U_I PRE_FORMAT_10_ITERATION = 2000; ///< fixed value used for key derivation before archive format 10 }; } // end of namespace #endif dar-2.7.15/src/libdar/crypto.hpp0000644000175000017500000000640214636066467013376 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file crypto.hpp /// \brief the crypto algoritm definition /// \ingroup API #ifndef CRYPTO_HPP #define CRYPTO_HPP extern "C" { } #include "../my_config.h" #include #include "datetime.hpp" #include namespace libdar { /// \addtogroup API /// @{ /// the different cypher available for encryption (strong or weak) enum class crypto_algo { none, ///< no encryption scrambling, ///< scrambling weak encryption blowfish, ///< blowfish strong encryption aes256, ///< AES 256 strong encryption twofish256, ///< twofish 256 strong encryption serpent256, ///< serpent 256 strong encryption camellia256 ///< camellia 256 strong encryption }; /// signator status struct signator { enum result_t { good, //< good signature bad, //< key correct bug signature tempered unknown_key, //< no key found to check the signature error //< signature failed to be checked for other error }; enum key_validity_t { valid, //< the key we have is neither expired nor revoked expired, //< the key we have has expired revoked //< the key we have has been revoked }; key_validity_t key_validity; //< validity of the key used to verify the signature result_t result; //< status of the signing std::string fingerprint; //< fingerprint of the key datetime signing_date; //< date of signature datetime signature_expiration_date; //< date of expiration of this signature bool operator < (const signator & ref) const { return fingerprint < ref.fingerprint; }; bool operator == (const signator & ref) const { return result == ref.result && key_validity == ref.key_validity && fingerprint == ref.fingerprint && signature_expiration_date == ref.signature_expiration_date; }; }; /// convert crypto algo to readable std::string extern std::string crypto_algo_2_string(crypto_algo algo); /// convert crypto algo to char extern char crypto_algo_2_char(crypto_algo a); /// convert char to crypto algo extern crypto_algo char_2_crypto_algo(char a); /// return whether the two signators lists match extern bool same_signatories(const std::list & a, const std::list & b); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/pile.hpp0000644000175000017500000002201414636066467013004 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file pile.hpp /// \brief class pile definition. Used to manage a stack of generic_file objects /// \ingroup Private #ifndef PILE_HPP #define PILE_HPP #include "../my_config.h" #include #include "generic_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// stores a stack of generic_files writing/reading on each others class pile : public generic_file { public: /// the constructor /// \note the mode (gf_mode) of the pile is the one of the first object pushed on the stack, /// thus when empty we choose the arbitrary gf_read_only value, because the stack is empty. pile() : generic_file(gf_read_only) { stack.clear(); }; pile(const pile & ref) = delete; pile(pile && ref) noexcept = delete; pile & operator = (const pile & ref) = delete; pile & operator = (pile && ref) noexcept = delete; ~pile() { detruit(); }; /// add a generic_file on the top /// \param[in] f is the address of the object to add to the stack /// \param[in] label unique label associated to this object in the current stack, exception thrown if label already used in stack /// \param[in] extend_mode allow the new object to have more read/write permission than the already placed object, which have the effect to change the read/write permission of the stack itself, future push() will accept wider permission even if extend_mode is not set /// \note once added, the object memory allocation is managed by the pile object /// the pile is responsible of destroying this object if its destructor is called /// however, the pile object releases its responsibility about any object that /// will be poped (see pop() below) from the stack. /// \note empty label (empty string) is the only identical value that can be used for several objects of the stack void push(generic_file *f, const std::string & label = "", bool extend_mode = false); /// remove the top generic_file from the top /// \returns the address of the object that has been poped from the stack or nullptr if the stack is empty /// \note this is now the duty of the caller to release this object memory when the object is no more needed generic_file *pop(); /// remove the top generic_file and destroy it /// \param[in] ptr is the type of the object that must be found on the top of the stack, /// It may also be the type of a parent class. Note that the value of the pointer is ignored. /// \return true if and only if the object on the top of the stack could be matched to the given type, this object is then poped from the stack and destroyed. template bool pop_and_close_if_type_is(T *ptr); /// returns the address of the top generic_file generic_file *top() const { if(stack.empty()) return nullptr; else return stack.back().ptr; }; /// returns the address of the bottom generic_file generic_file *bottom() const { if(stack.empty()) return nullptr; else return stack[0].ptr; }; /// returns the number of objects in the stack U_I size() const { return stack.size(); }; /// returns true if the stack is empty, false otherwise. bool is_empty() const { return stack.empty(); }; /// clears the stack void clear() { detruit(); }; /// this template let the class user find out the higher object on the stack of the given type /// \param[in,out] ref gives the type of the object to look for, and gets the address of the first object found starting from the top /// \note if no object of that type could be found in the stack ref is set to nullptr template void find_first_from_top(T * & ref) const; /// this template is similar to the template "find_first_from_top" except that the search is started from the bottom of the stack template void find_first_from_bottom(T * & ref) const; /// return the generic_file object just below the given object or nullptr if the object is at the bottom of the stack or is not in the stack generic_file *get_below(const generic_file *ref); /// return the generic_file object just above the given object or nullptr if the object is at the bottom of the stack or is not in the stack generic_file *get_above(const generic_file *ref); /// find the object associated to a given label /// \param[in] label is the label to look for, empty string is forbidden /// \return the object associated to label, else an exception is thrown generic_file *get_by_label(const std::string & label); /// if label is associated to a member of the stack, makes this member of the stack an anoymous member (the label is no more associated to this object, while this object stays in the stack untouched /// \param[in] label the label to clear, empty string is not a valid label an exception is thrown if used here /// \note no exception is thrown else, even if the label is not present in the stack void clear_label(const std::string & label); /// associate a additional label to the object currently at the top of the stack /// \param[in] label the label to add /// \note this does not remove an eventually existing label that had been added either by push() or add_label() previously /// \note an object of the stack can thus be refered by several different labels void add_label(const std::string & label); /// call the generic_file::sync_write() method of all object found above ptr in the stack void sync_write_above(generic_file *ptr); /// call the generic_file::flush_read() method of all objects found above ptr in the stack void flush_read_above(generic_file *ptr); // inherited methods from generic_file // they all apply to the top generic_file object, they fail by Erange() exception if the stack is empty virtual bool skippable(skippability direction, const infinint & amount) override; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & amount) const override; virtual infinint get_position() const override; void copy_to(generic_file & ref) override; void copy_to(generic_file & ref, const infinint & crc_size, crc * & value) override; protected: virtual void inherited_read_ahead(const infinint & amount) override; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override; virtual void inherited_flush_read() override; virtual void inherited_terminate() override; private: struct face { generic_file * ptr; std::list labels; }; // ok, had not much idea to find a name for that struct, "face" was the first idea found to be associated with "pile", which means stack // in French but also is the name of the face of a coin where its value is written. The opposite face of a coin is called "face" in French // because often a face is design there and the expression "tirer `a pile ou face" (meaning "to toss up") is very common. std::deque stack; void detruit(); std::deque::iterator look_for_label(const std::string & label); }; template bool pile::pop_and_close_if_type_is(T *ptr) { generic_file *top = nullptr; if(!stack.empty()) { top = stack.back().ptr; ptr = dynamic_cast(top); if(ptr != nullptr) { ptr->terminate(); stack.pop_back(); delete ptr; return true; } else return false; } else return false; } template void pile::find_first_from_top(T * & ref) const { ref = nullptr; for(std::deque::const_reverse_iterator it = stack.rbegin(); it != stack.rend() && ref == nullptr; ++it) ref = dynamic_cast(it->ptr); } template void pile::find_first_from_bottom(T * & ref) const { ref = nullptr; for(std::deque::const_iterator it = stack.begin(); it != stack.end() && ref == nullptr; ++it) ref = dynamic_cast(it->ptr); } /// @} } // end of namespace #endif dar-2.7.15/src/libdar/tronconneuse.cpp0000644000175000017500000004315414636066467014600 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif } #include "tronconneuse.hpp" #include "tools.hpp" #include "memory_file.hpp" using namespace std; namespace libdar { tronconneuse::tronconneuse(U_32 block_size, generic_file & encrypted_side, const archive_version & x_reading_ver, std::unique_ptr & ptr) : proto_tronco(encrypted_side.get_mode() == gf_read_only ? gf_read_only : gf_write_only) { if(block_size == 0) throw Erange("tronconneuse::tronconneuse", tools_printf(gettext("%d is not a valid block size"), block_size)); buf_offset = 0; buf_byte_data = 0; buf_size = 0; buf = nullptr; // cannot invoke pure virtual methods from constructor clear_block_size = block_size; current_position = 0; initial_shift = 0; block_num = 0; encrypted = & encrypted_side; encrypted_buf = nullptr; // cannot invoke pure virtual methods from constructor encrypted_buf_data = 0; encrypted_buf_size = 0; extra_buf_size = 0; extra_buf_data = 0; extra_buf = nullptr; weof = false; reof = false; reading_ver = x_reading_ver; crypto = move(ptr); if(!crypto) throw Erange("tronconneuse::tronconneuse", "null pointer given as crypto_module to tronconneuse"); trailing_clear_data = nullptr; // buffers cannot be initialized here as they need result from pure virtual methods // the inherited class constructor part has not yet been initialized // for this reason C++ forbids us to call virtual methods (they may rely on data that // has not been initialized. } bool tronconneuse::skippable(skippability direction, const infinint & amount) { if(is_terminated()) throw SRC_BUG; if(encrypted->get_mode() != gf_read_only) return false; else { if(current_position < buf_offset || buf_offset + infinint(buf_byte_data) <= current_position) return encrypted->skippable(direction, amount); else return true; } } bool tronconneuse::skip(const infinint & pos) { bool ret = true; if(is_terminated()) throw SRC_BUG; if(encrypted->get_mode() != gf_read_only) throw SRC_BUG; if(current_position != pos) { if(pos < buf_offset) reof = false; current_position = pos; ret = check_current_position(); if(!ret) skip_to_eof(); } else ret = true; return ret; } bool tronconneuse::skip_to_eof() { bool ret; if(is_terminated()) throw SRC_BUG; if(encrypted->get_mode() != gf_read_only) throw SRC_BUG; ret = encrypted->skip_to_eof(); if(ret) { infinint residu; init_buf(); // to be sure encrypted_buf_size is defined if(encrypted->get_position() < initial_shift) throw SRC_BUG; // eof is before the first encrypted byte euclide(encrypted->get_position() - initial_shift, encrypted_buf_size, block_num, residu); current_position = block_num * infinint(clear_block_size); reof = false; // must be set to true to have fill_buf filling the clear buffer (void)fill_buf(); reof = true; // now fill_buf has completed we can set it to the expected value current_position = buf_offset + infinint(buf_byte_data); // we are now at the eof ret = encrypted->skip_to_eof(); } return ret; } bool tronconneuse::skip_relative(S_I x) { bool ret; if(is_terminated()) throw SRC_BUG; if(encrypted->get_mode() != gf_read_only) throw SRC_BUG; if(x >= 0) ret = skip(current_position + x); else { x = -x; if(current_position >= x) ret = skip(current_position - infinint(x)); else { skip(0); ret = false; } } return ret; } void tronconneuse::inherited_read_ahead(const infinint & amount) { infinint new_amount = 0; U_32 interim = 0; infinint x_amount = amount; if(current_position > buf_offset) { // byte offset in the buffer where will take place the next read operation new_amount = current_position - buf_offset; if(new_amount < buf_byte_data) // some data remain to be read in buffer { // temporarily using new_amount // to store the amount of readable data in the buffer new_amount = infinint(buf_byte_data) - new_amount; if(x_amount <= new_amount) return; // nothing to do, the read_ahead is entirely contained in the buffer else x_amount -= new_amount; } new_amount = 0; } // else current position is before the buffer, ignoring data actually in the buffer while(!x_amount.is_zero()) { interim = 0; x_amount.unstack(interim); new_amount += crypto->encrypted_block_size_for(interim); } encrypted->read_ahead(new_amount); } U_I tronconneuse::inherited_read(char *a, U_I size) { U_I lu = 0; bool eof = false; U_32 pos_in_buf; while(lu < size && ! eof) { pos_in_buf = fill_buf(); if(pos_in_buf >= buf_byte_data) eof = true; else // some data can be read { while(pos_in_buf < buf_byte_data && lu < size) a[lu++] = buf[pos_in_buf++]; current_position = buf_offset + infinint(pos_in_buf); } } return lu; } void tronconneuse::inherited_write(const char *a, U_I size) { U_I lu = 0; bool thread_stop = false; Ethread_cancel caught = Ethread_cancel(false, 0); if(weof) throw SRC_BUG; // write_end_of_file has been called no further write is allowed init_buf(); while(lu < size) { U_I place = clear_block_size - buf_byte_data; // how much we are able to store before completing the current block U_I avail = size - lu; // how much data is available U_I min = avail > place ? place : avail; // the minimum(place, avail) (void)memcpy(buf + buf_byte_data, a + lu, min); buf_byte_data += min; lu += min; if(buf_byte_data >= clear_block_size) // we have a complete buffer to encrypt { try { flush(); } catch(Ethread_cancel & e) { thread_stop = true; caught = e; } block_num++; } } current_position += infinint(size); if(thread_stop) throw caught; } void tronconneuse::detruit() noexcept { if(buf != nullptr) { delete [] buf; buf = nullptr; } if(encrypted_buf != nullptr) { delete [] encrypted_buf; encrypted_buf = nullptr; } if(extra_buf != nullptr) { delete [] extra_buf; extra_buf = nullptr; } buf_size = 0; buf_byte_data = 0; encrypted_buf_size = 0; encrypted_buf_data = 0; extra_buf_size = 0; crypto.reset(); extra_buf_data = 0; } void tronconneuse::nullifyptr() noexcept { buf = nullptr; encrypted = nullptr; encrypted_buf = nullptr; extra_buf = nullptr; } void tronconneuse::copy_from(const tronconneuse & ref) { nullifyptr(); if(is_terminated()) throw SRC_BUG; try { initial_shift = ref.initial_shift; buf_offset = ref.buf_offset; buf_byte_data = ref.buf_byte_data; buf_size = ref.buf_size; buf = new (nothrow) char[buf_size]; if(buf == nullptr) throw Ememory("tronconneuse::copy_from"); (void)memcpy(buf, ref.buf, buf_byte_data); clear_block_size = ref.clear_block_size; current_position = ref.current_position; block_num = ref.block_num; encrypted = ref.encrypted; // objets share the same generic_file reference encrypted_buf_size = ref.encrypted_buf_size; encrypted_buf_data = ref.encrypted_buf_data; encrypted_buf = new (nothrow) char[encrypted_buf_size]; if(encrypted_buf == nullptr) throw Ememory("tronconneuse::copy_from"); (void)memcpy(encrypted_buf, ref.encrypted_buf, encrypted_buf_data); extra_buf_size = ref.extra_buf_size; extra_buf_data = ref.extra_buf_data; extra_buf = new (nothrow) char[extra_buf_size]; if(extra_buf == nullptr) throw Ememory("tronconneuse::copy_from"); (void)memcpy(extra_buf, ref.extra_buf, extra_buf_data); weof = ref.weof; reof = ref.reof; reading_ver = ref.reading_ver; if(ref.crypto) crypto = ref.crypto->clone(); else crypto.reset(); trailing_clear_data = ref.trailing_clear_data; } catch(...) { detruit(); throw; } } void tronconneuse::move_from(tronconneuse && ref) noexcept { initial_shift = move(ref.initial_shift); buf_offset = move(ref.buf_offset); buf_byte_data = move(ref.buf_byte_data); buf_size = move(ref.buf_size); std::swap(buf, ref.buf); clear_block_size = move(ref.clear_block_size); current_position = move(ref.current_position); block_num = move(ref.block_num); encrypted = ref.encrypted; // yes, we copy the pointed to object address, here encrypted_buf_size = move(ref.encrypted_buf_size); encrypted_buf_data = move(ref.encrypted_buf_data); std::swap(encrypted_buf, ref.encrypted_buf); extra_buf_offset = move(ref.extra_buf_offset); extra_buf_size = move(ref.extra_buf_size); std::swap(extra_buf, ref.extra_buf); weof = move(ref.weof); reof = move(ref.reof); reading_ver = move(ref.reading_ver); crypto = move(ref.crypto); trailing_clear_data = move(ref.trailing_clear_data); } U_32 tronconneuse::fill_buf() { U_32 ret; infinint crypt_offset; infinint tmp_ret; if(current_position < buf_offset || ((buf_offset + infinint(buf_byte_data)) <= current_position && !reof)) // requested data not in current clear buffer { position_clear2crypt(current_position, crypt_offset, buf_offset, tmp_ret, block_num); if(!reof) { // if extra_buf contains the encrypted byte we need we move them to encrypted_buf if(crypt_offset >= extra_buf_offset && crypt_offset < extra_buf_offset + extra_buf_data) { memcpy(encrypted_buf, extra_buf, extra_buf_data); encrypted_buf_data = extra_buf_data; extra_buf_data = 0; } else // else we empty encrypted_buf and extra_buf { extra_buf_data = 0; encrypted_buf_data = 0; } // we skip at the beginning of the crypted block plus the already read bytes from extra_buf if(!encrypted->skip(crypt_offset + initial_shift + encrypted_buf_data)) buf_byte_data = 0; // we can now read the remaining data to complete the crypto block encrypted_buf_data += encrypted->read(encrypted_buf, encrypted_buf_size - encrypted_buf_data); if(encrypted_buf_data < encrypted_buf_size) { reof = true; remove_trailing_clear_data_from_encrypted_buf(crypt_offset); } try { buf_byte_data = crypto->decrypt_data(block_num, encrypted_buf, encrypted_buf_data, buf, buf_size); } catch(Erange & e) { if(!reof) { try { remove_trailing_clear_data_from_encrypted_buf(crypt_offset); // retrying but without trailing cleared data buf_byte_data = crypto->decrypt_data(block_num, encrypted_buf, encrypted_buf_data, buf, buf_size); } catch(Egeneric & f) { throw e; } } else // already tried to remove clear data at end of encrypted buffer throw; } if(buf_byte_data > buf_size) { buf_byte_data = clear_block_size; throw Erange("tronconneuse::fill_buff", gettext("Data corruption may have occurred, cannot decrypt data")); } } else buf_byte_data = 0; // no data could be read as the requested position could not be reached } else tmp_ret = current_position - buf_offset; ret = 0; tmp_ret.unstack(ret); if(!tmp_ret.is_zero()) throw SRC_BUG; // result is modulo clear_block_size which is U_32, thus it should fit in U_32 (=ret) return ret; } void tronconneuse::flush() { if(encrypted->get_mode() != gf_write_only) return; if(weof) return; if(buf_byte_data > 0) { init_buf(); encrypted_buf_data = crypto->encrypt_data(block_num, buf, buf_byte_data, buf_size, encrypted_buf, encrypted_buf_size); try { encrypted->write(encrypted_buf, encrypted_buf_data); buf_byte_data = 0; buf_offset += infinint(clear_block_size); } catch(Ethread_cancel & e) { // at this step, the pending encrypted data could not be wrote to disk // (Ethread_cancel exception has been thrown before). // But we have returned to the upper layer the position of the corresponding clear // data which may have already been recorded // in the catalogue above, thus we cannot just drop this pending encrypted data // as it would make reference to inexistant data in the catalogue. // By chance the thread_cancellation mechanism in clean termination is now carried // by the exception we just caugth here so we may retry to write the encrypted data // no new Ethread_cancel exception will be thrown this time. encrypted->write(encrypted_buf, encrypted_buf_data); buf_byte_data = 0; buf_offset += infinint(clear_block_size); // Now that we have cleanly completed the action at this level, // we can propagate the exception toward the caller throw; } } } void tronconneuse::init_buf() { if(encrypted_buf == nullptr) { encrypted_buf_data = 0; encrypted_buf_size = crypto->encrypted_block_size_for(clear_block_size); encrypted_buf = new (nothrow) char[encrypted_buf_size]; if(encrypted_buf == nullptr) { encrypted_buf_size = 0; throw Ememory("tronconneuse::init_encrypte_buf_size"); } } if(buf == nullptr) { buf_byte_data = 0; buf_size = crypto->clear_block_allocated_size_for(clear_block_size); if(buf_size < clear_block_size) throw SRC_BUG; // buf_size must be larger than or equal to clear_block_size buf = new (nothrow) char[buf_size]; if(buf == nullptr) { buf_size = 0; throw Ememory("tronconneuse::init_encrypte_buf_size"); } } if(extra_buf == nullptr) { extra_buf_data = 0; extra_buf_size = encrypted_buf_size; // using same size as encrypted_buf extra_buf = new (nothrow) char[extra_buf_size]; if(extra_buf == nullptr) { extra_buf_size = 0; throw Ememory("tronconneuse::init_encrypte_buf_size"); } } } void tronconneuse::position_clear2crypt(const infinint & pos, infinint & file_buf_start, infinint & clear_buf_start, infinint & pos_in_buf, infinint & block_num) { euclide(pos, clear_block_size, block_num, pos_in_buf); init_buf(); // needs to be placed before the following line as encrypted_buf_size is defined by this call file_buf_start = block_num * infinint(encrypted_buf_size); clear_buf_start = block_num * infinint(clear_block_size); } void tronconneuse::position_crypt2clear(const infinint & pos, infinint & clear_pos) { infinint block, residu; init_buf(); // needs to be placed before the following line as encrypted_buf_size is defined by this call euclide(pos, encrypted_buf_size, block, residu); clear_pos = block * infinint(clear_block_size) + residu; } void tronconneuse::remove_trailing_clear_data_from_encrypted_buf(const infinint & crypt_offset) { if(encrypted == nullptr) throw SRC_BUG; if(trailing_clear_data != nullptr) { infinint clear_offset = 0; bool notfound = false; if(extra_buf_data > 0) throw SRC_BUG; if(!reof) { extra_buf_offset = encrypted->get_position(); extra_buf_data = encrypted->read(extra_buf, extra_buf_size); } try { memory_file tmp; tmp.write(encrypted_buf, encrypted_buf_data); if(extra_buf_data > 0) tmp.write(extra_buf, extra_buf_data); clear_offset = (*trailing_clear_data)(tmp, reading_ver); // this is the offset that is written in the archive // it is thus not relative to tmp but to the real // sub layer including the initial_shift bytes if(clear_offset >= initial_shift) clear_offset -= initial_shift; // now clear_offset can be compared to crypt_offset else notfound = true; } catch(Egeneric & e) { if(reof) { encrypted_buf_data = 0; extra_buf_data = 0; } notfound = true; } catch(...) { throw SRC_BUG; } if(notfound) return; if(crypt_offset >= clear_offset) // all data in encrypted_buf is clear data { encrypted_buf_data = 0; extra_buf_data = 0; reof = true; } else { U_I nouv_buf_data = 0; clear_offset -= crypt_offset; clear_offset.unstack(nouv_buf_data); if(!clear_offset.is_zero()) throw SRC_BUG; // cannot handle that integer as U_32 while this number should be less than encrypted_buf_size which is a U_32 if(nouv_buf_data <= encrypted_buf_data) { encrypted_buf_data = nouv_buf_data; extra_buf_data = 0; reof = true; } else throw SRC_BUG; // more encrypted data than could be read so far! } } // else, nothing can be done } } // end of namespace dar-2.7.15/src/libdar/fsa_family.cpp0000644000175000017500000000673314636066467014172 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "erreurs.hpp" #include "tools.hpp" #include "fsa_family.hpp" using namespace std; namespace libdar { string fsa_family_to_string(fsa_family f) { switch(f) { case fsaf_hfs_plus: return "HFS+"; case fsaf_linux_extX: return "ext2/3/4"; default: throw SRC_BUG; } } string fsa_nature_to_string(fsa_nature n) { switch(n) { case fsan_unset: throw SRC_BUG; case fsan_creation_date: return gettext("creation date"); case fsan_append_only: return gettext("append only"); case fsan_compressed: return gettext("compressed"); case fsan_no_dump: return gettext("no dump flag"); case fsan_immutable: return gettext("immutable"); case fsan_data_journaling: return gettext("journalized"); case fsan_secure_deletion: return gettext("secure deletion"); case fsan_no_tail_merging: return gettext("no tail merging"); case fsan_undeletable: return gettext("undeletable"); case fsan_noatime_update: return gettext("no atime update"); case fsan_synchronous_directory: return gettext("synchronous directory"); case fsan_synchronous_update: return gettext("synchronous update"); case fsan_top_of_dir_hierarchy: return gettext("top of directory hierarchy"); default: throw SRC_BUG; } } fsa_scope all_fsa_families() { fsa_scope ret; ret.insert(fsaf_hfs_plus); ret.insert(fsaf_linux_extX); return ret; } const U_I FSA_SCOPE_BIT_HFS_PLUS = 1; const U_I FSA_SCOPE_BIT_LINUX_EXTX = 2; infinint fsa_scope_to_infinint(const fsa_scope & val) { infinint ret = 0; if(val.find(fsaf_hfs_plus) != val.end()) ret |= FSA_SCOPE_BIT_HFS_PLUS; if(val.find(fsaf_linux_extX) != val.end()) ret |= FSA_SCOPE_BIT_LINUX_EXTX; return ret; } fsa_scope infinint_to_fsa_scope(const infinint & ref) { fsa_scope ret; ret.clear(); if((ref & FSA_SCOPE_BIT_HFS_PLUS) != 0) ret.insert(fsaf_hfs_plus); if((ref & FSA_SCOPE_BIT_LINUX_EXTX) != 0) ret.insert(fsaf_linux_extX); return ret; } string fsa_scope_to_string(bool saved, const fsa_scope & scope) { string ret = ""; // first letter if(scope.find(fsaf_hfs_plus) != scope.end()) if(saved) ret += "H"; else ret += "h"; else ret += "-"; // second letter if(scope.find(fsaf_linux_extX) != scope.end()) if(saved) ret += "L"; else ret += "l"; else ret += "-"; return ret; } } // end of namespace dar-2.7.15/src/libdar/entrepot.hpp0000644000175000017500000001605514636067146013716 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file entrepot.hpp /// \brief defines the entrepot interface. /// /// Entrepot interface defines a generic way to interact with files (slices) /// on a filesystem. It is used to instantiate file-like objects (from classes inherited /// from class fichier_global), in order to read or write data to such files. /// \note only the constructors of class entrepot are part of libdar API /// the other methods may change without notice and are not expected to be used /// from external applications. /// \ingroup API #ifndef ENTREPOT_HPP #define ENTREPOT_HPP #include "../my_config.h" #include #include #include "user_interaction.hpp" #include "path.hpp" #include "archive_aux.hpp" #include "gf_mode.hpp" namespace libdar { /// \addtogroup API /// @{ // no need to dig into this from API header class fichier_global; /// the Entrepot interface class entrepot { public: /// constructor entrepot(); /// copy constructor entrepot(const entrepot & ref) = default; /// move constructor entrepot(entrepot && ref) noexcept = default; /// assignment operator entrepot & operator = (const entrepot & ref) = default; /// move operator entrepot & operator = (entrepot && ref) noexcept = default; /// destructor virtual ~entrepot() = default; /// says whether two entrepot objects points to the same location bool operator == (const entrepot & ref) const { return get_url() == ref.get_url(); }; /// defines the directory where to proceed to future open() -- this is a "chdir" semantics virtual void set_location(const path & chemin); /// defines the root to use if set_location is given a relative path virtual void set_root(const path & p_root); /// returns the full path of location /// \note this is equivalent to get_root()/get_location() if get_location() is relative path else get_location() virtual path get_full_path() const; /// full path of current directory + anything necessary to provide URL formated information virtual std::string get_url() const = 0; /// set default ownership for files to be created thanks to the open() methods void set_user_ownership(const std::string & x_user) { user = x_user; }; void set_group_ownership(const std::string & x_group) { group = x_group; }; virtual const path & get_location() const { return where; }; //< retreives relative to root path the current location points to virtual const path & get_root() const { return root; }; //< retrieves the given root location const std::string & get_user_ownership() const { return user; }; const std::string & get_group_ownership() const { return group; }; /// defines the way to open a file and return a "class fichier_global" object as last argument upon success /// \param[in] dialog for user interaction /// \param[in] filename is the full path+name of the file to open (read/create/write to) /// \param[in] mode defines which way to open the file (read-only, read-write or write-only) /// \param[in] force_permission whether to set the file permission to the value given in the permission argument /// \param[in] permission if force_permission is set, change the file permission to that value /// \param[in] fail_if_exists tells whether the underlying implementation have to fail throwing Erange("exists") if the file already exist when write access is required /// \param[in] erase tells whether the underlying implementation will empty an existing file before writing to it /// \param[in] algo defines the hash file to create, other value than hash_none are accepted only in writeonly mode with erase or fail_if_exist set /// \param[in] provide_a_plain_file if true a plainly seekable object is returned which mimics the available features of a plain file, else a pipe-like object which has limited seek features but which can still record the offset position is returned and is suitable to be used on real named/anonymous pipes (if the provided filename is expected to be of such type for example) /// \return upon success returns an object from a class inherited from fichier_global that the caller has the duty to delete, else an exception is thrown (most of the time it should be a Esystem object) /// by the called inherited class fichier_global *open(const std::shared_ptr & dialog, const std::string & filename, gf_mode mode, bool force_permission, U_I permission, bool fail_if_exists, bool erase, hash_algo algo, bool provide_a_plain_file = true) const; /// routines to read existing files in the current directory (see set_location() / set_root() methods) virtual void read_dir_reset() const = 0; virtual bool read_dir_next(std::string & filename) const = 0; void unlink(const std::string & filename) const { inherited_unlink(filename); }; //< done this way for homogeneity with open/inherited_open /// generate a clone of "this" /// \deprecated this method will disapear in the future it is only kept /// there to allow the APIv5 adaptation layer to work over APIv6 virtual entrepot *clone() const = 0; protected: virtual fichier_global *inherited_open(const std::shared_ptr & dialog, //< for user interaction const std::string & filename, //< filename to open gf_mode mode, //< mode to use bool force_permission, //< set the permission of the file to open U_I permission, //< value of the permission to assign when force_permission is true bool fail_if_exists, //< whether to fail if file exists (write mode) bool erase) const = 0; //< whether to erase file if file already exists (write mode) virtual void inherited_unlink(const std::string & filename) const = 0; virtual void read_dir_flush() = 0; //< ends the read_dir_next, (no more entry available) private: path where; path root; std::string user; std::string group; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/block_compressor.cpp0000644000175000017500000001746114636066467015426 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } #include "block_compressor.hpp" #include "erreurs.hpp" #include "compress_block_header.hpp" using namespace std; namespace libdar { block_compressor::block_compressor(unique_ptr block_zipper, generic_file & compressed_side, U_I uncompressed_bs): proto_compressor((compressed_side.get_mode() == gf_read_only)? gf_read_only: gf_write_only), zipper(move(block_zipper)), compressed(&compressed_side), uncompressed_block_size(uncompressed_bs) { U_I compr_bs = zipper->get_min_size_to_compress(uncompressed_block_size); // sanity checks on fields set by constructors if(get_mode() == gf_read_write) throw SRC_BUG; // mode not suported for this type of object if(!zipper) throw SRC_BUG; if(compressed == nullptr) throw SRC_BUG; if(uncompressed_block_size < min_uncompressed_block_size) throw SRC_BUG; // initializing simple fields not set by constructors suspended = false; need_eof = false; current = make_unique(compr_bs, uncompressed_block_size); reof = false; } block_compressor::~block_compressor() { try { terminate(); } catch(...) { // ignore all exceptions } } void block_compressor::suspend_compression() { if(!suspended) { inherited_sync_write(); inherited_flush_read(); suspended = true; } } void block_compressor::resume_compression() { suspended = false; } bool block_compressor::skippable(skippability direction, const infinint & amount) { if(is_terminated()) throw SRC_BUG; return compressed->skippable(direction, amount); } bool block_compressor::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; current->reset(); reof = false; need_eof = false; return compressed->skip(pos); } bool block_compressor::skip_to_eof() { if(is_terminated()) throw SRC_BUG; current->reset(); reof = false; need_eof = false; return compressed->skip_to_eof(); } bool block_compressor::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; current->reset(); reof = false; need_eof = false; return compressed->skip_relative(x); } bool block_compressor::truncatable(const infinint & pos) const { return compressed->truncatable(pos); } infinint block_compressor::get_position() const { switch(get_mode()) { case gf_read_only: if(!current->clear_data.all_is_read()) throw SRC_BUG; break; case gf_write_only: if(!current->clear_data.is_empty()) throw SRC_BUG; break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } return compressed->get_position(); } U_I block_compressor::inherited_read(char *a, U_I size) { if(is_terminated()) throw SRC_BUG; if(suspended) { if(!reof) return compressed->read(a, size); else return 0; } else { U_I ret = 0; while(ret < size && ! reof) { if(current->clear_data.all_is_read()) read_and_uncompress_current(); ret += current->clear_data.read(a + ret, size - ret); } return ret; } } void block_compressor::inherited_write(const char *a, U_I size) { U_I wrote = 0; if(is_terminated()) throw SRC_BUG; if(suspended) { compressed->write(a, size); } else { while(wrote < size) { wrote += current->clear_data.write(a + wrote, size - wrote); if(current->clear_data.is_full()) compress_and_write_current(); } if(size > 0) need_eof = true; } } void block_compressor::inherited_truncate(const infinint & pos) { if(is_terminated()) throw SRC_BUG; compressed->truncate(pos); } void block_compressor::inherited_sync_write() { if(is_terminated()) throw SRC_BUG; if(get_mode() == gf_read_only) return; compress_and_write_current(); if(need_eof) { compress_block_header bh; bh.type = compress_block_header::H_EOF; bh.size = 0; bh.dump(*compressed); // adding EOF null block size need_eof = false; } } void block_compressor::inherited_terminate() { switch(get_mode()) { case gf_read_only: break; case gf_write_only: inherited_sync_write(); break; case gf_read_write: throw SRC_BUG; default: throw SRC_BUG; } } void block_compressor::compress_and_write_current() { compress_block_header bh; if(current->clear_data.get_data_size() > 0) { current->crypted_data.set_data_size(zipper->compress_data(current->clear_data.get_addr(), current->clear_data.get_data_size(), current->crypted_data.get_addr(), current->crypted_data.get_max_size())); if(current->crypted_data.get_data_size() > 0) { bh.type = compress_block_header::H_DATA; bh.size = current->crypted_data.get_data_size(); } else throw SRC_BUG; // it is not expected that compressed data, that to say some information // get compressed as a result of no information at all try { bh.dump(*compressed); compressed->write(current->crypted_data.get_addr(), current->crypted_data.get_data_size()); current->reset(); } catch(Ethread_cancel & e) { current->reset(); throw; } catch(Euser_abort & e) { current->reset(); throw; } } } void block_compressor::read_and_uncompress_current() { U_I bs; compress_block_header bh; if(!bh.set_from(*compressed)) { current->reset(); reof = true; return; } switch(bh.type) { case compress_block_header::H_DATA: bs = 0; bh.size.unstack(bs); if(!bh.size.is_zero()) throw Erange("zip_below_read::work", gettext("incoherent compressed block structure, compressed data corruption")); if(bs > current->crypted_data.get_max_size()) throw Erange("zip_below_read::work", gettext("incoherent compressed block structure, compressed block size in archive too large")); if(bs > 0) { current->crypted_data.set_data_size(compressed->read(current->crypted_data.get_addr(), bs)); current->clear_data.set_data_size(zipper->uncompress_data(current->crypted_data.get_addr(), current->crypted_data.get_data_size(), current->clear_data.get_addr(), current->clear_data.get_max_size())); current->clear_data.rewind_read(); } else throw Erange("zip_below_read::work", gettext("incoherent compressed block structure, compressed data corruption")); break; case compress_block_header::H_EOF: if(!bh.size.is_zero()) throw Erange("zip_below_read::work", gettext("incoherent compressed block structure, compressed data corruption")); current->reset(); reof = true; break; default: throw Erange("zip_below_read::work", gettext("incoherent compressed block structure, compressed data corruption")); } } } // end of namespace dar-2.7.15/src/libdar/semaphore.cpp0000644000175000017500000001034214636066467014032 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "erreurs.hpp" #include "tools.hpp" #include "deci.hpp" #include "cat_all_entrees.hpp" #include "semaphore.hpp" using namespace std; namespace libdar { semaphore::semaphore(const std::shared_ptr & dialog, const string & backup_hook_file_execute, const mask & backup_hook_file_mask) : mem_ui(dialog) { count = 0; chem = ""; filename = ""; uid = 0; gid = 0; sig = '\0'; execute = backup_hook_file_execute; match = backup_hook_file_mask.clone(); if(match == nullptr) throw Ememory("semaphore::semaphore"); } void semaphore::raise(const string & x_chem, const cat_entree *object, bool data_to_save) { if(count == 1) throw SRC_BUG; if(count > 1) // outer directory under controlled backup (hook_execute() ran in "start" context) { if(dynamic_cast(object) != nullptr) if(data_to_save) --count; else throw SRC_BUG; // CAT_EOD should always have its data to be saved else { if(dynamic_cast(object) != nullptr) ++count; } } else // semaphore is ready for a new entry { const cat_nomme *o_nom = dynamic_cast(object); if(o_nom == nullptr) return; // CAT_EOD associated to an unsaved directory if(data_to_save && match->is_covered(x_chem)) { const cat_directory *o_dir = dynamic_cast(object); const cat_inode *o_ino = dynamic_cast(object); if(o_dir != nullptr) count = 2; else count = 1; chem = x_chem; filename = o_nom->get_name(); uid = o_ino != nullptr ? o_ino->get_uid() : 0; gid = o_ino != nullptr ? o_ino->get_gid() : 0; sig = object->signature(); tools_hook_execute(get_ui(), build_string("start")); } } } void semaphore::lower() { if(count == 1) { count = 0; tools_hook_execute(get_ui(), build_string("end")); } } string semaphore::build_string(const string & context) { map corres; corres['%'] = "%"; corres['p'] = chem; corres['f'] = filename; corres['c'] = context; corres['t'] = tools_printf("%c", sig); try { deci gid_string = gid; deci uid_string = uid; corres['u'] = uid_string.human(); corres['g'] = gid_string.human(); } catch(Edeci & e) { throw Edeci("semaphore::build_string", string(gettext("Error while converting UID/GID to string for backup hook file: ")) + e.get_message()); } return tools_substitute(execute, corres); } void semaphore::copy_from(const semaphore & ref) { count = ref.count; chem = ref.chem; filename = ref.filename; uid = ref.uid; gid = ref.gid; sig = ref.sig; execute = ref.execute; if(ref.match == nullptr) throw SRC_BUG; match = ref.match->clone(); if(match == nullptr) throw Ememory("semaphore::copy_from"); } void semaphore::move_from(semaphore && ref) noexcept { count = move(ref.count); chem = move(ref.chem); filename = move(ref.filename); uid = move(ref.uid); gid = move(ref.gid); sig = move(ref.sig); execute = move(ref.execute); swap(match, ref.match); } void semaphore::detruit() { if(match != nullptr) { delete match; match = nullptr; } } } // end of namespace dar-2.7.15/src/libdar/cat_entree.cpp0000644000175000017500000003516714636255166014170 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_entree.hpp" #include "cat_all_entrees.hpp" #include "macro_tools.hpp" #include "cat_signature.hpp" #include "tools.hpp" using namespace std; namespace libdar { const U_I cat_entree::ENTREE_CRC_SIZE = 2; cat_entree *cat_entree::read(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, entree_stats & stats, std::map & corres, compression default_algo, bool lax, bool only_detruit, bool small) { char type; saved_status saved; cat_entree *ret = nullptr; map ::iterator it; infinint tmp; bool read_crc; generic_file *ptr = nullptr; pdesc->check(small); if(small) { ptr = pdesc->esc; pdesc->stack->flush_read_above(pdesc->esc); } else ptr = pdesc->stack; read_crc = small && !ptr->crc_status(); // crc may be activated when reading a hard link (mirage read, now reading the inode) if(read_crc) ptr->reset_crc(ENTREE_CRC_SIZE); try { try { cat_signature cat_sig(*ptr, reading_ver); if(!cat_sig.get_base_and_status((unsigned char &)type, saved)) { if(!lax) throw Erange("cat_entree::read", gettext("corrupted file")); else type = ' '; // used to by-pass object construction and return nullptr as value of this method } } catch(Erange & e) { type = ' '; // used to by-pass object construction and return nullptr as value of this method } switch(type) { case ' ': break; // returned value will be nullptr case 'f': ret = new (nothrow) cat_file(dialog, pdesc, reading_ver, saved, default_algo, small); break; case 'l': ret = new (nothrow) cat_lien(dialog, pdesc, reading_ver, saved, small); break; case 'c': ret = new (nothrow) cat_chardev(dialog, pdesc, reading_ver, saved, small); break; case 'b': ret = new (nothrow) cat_blockdev(dialog, pdesc, reading_ver, saved, small); break; case 'p': ret = new (nothrow) cat_tube(dialog, pdesc, reading_ver, saved, small); break; case 's': ret = new (nothrow) cat_prise(dialog, pdesc, reading_ver, saved, small); break; case 'd': ret = new (nothrow) cat_directory(dialog, pdesc, reading_ver, saved, stats, corres, default_algo, lax, only_detruit, small); break; case 'm': ret = new (nothrow) cat_mirage(dialog, pdesc, reading_ver, saved, stats, corres, default_algo, cat_mirage::fmt_mirage, lax, small); break; case 'h': // old hard-link object ret = new (nothrow) cat_mirage(dialog, pdesc, reading_ver, saved, stats, corres, default_algo, cat_mirage::fmt_hard_link, lax, small); break; case 'e': // old etiquette object ret = new (nothrow) cat_mirage(dialog, pdesc, reading_ver, saved, stats, corres, default_algo, lax, small); break; case 'z': if(saved != saved_status::saved) { if(!lax) throw Erange("cat_entree::read", gettext("corrupted file")); else dialog->message(gettext("LAX MODE: Unexpected saved status for end of directory entry, assuming data corruption occurred, ignoring and continuing")); } ret = new (nothrow) cat_eod(pdesc, small); break; case 'x': if(saved != saved_status::saved) { if(!lax) throw Erange("cat_entree::read", gettext("corrupted file")); else dialog->message(gettext("LAX MODE: Unexpected saved status for class \"cat_detruit\" object, assuming data corruption occurred, ignoring and continuing")); } ret = new (nothrow) cat_detruit(pdesc, reading_ver, small); break; case 'o': ret = new (nothrow) cat_door(dialog, pdesc, reading_ver, saved, default_algo, small); break; default : if(!lax) throw Erange("cat_entree::read", gettext("unknown type of data in catalogue")); else { dialog->message(gettext("LAX MODE: found unknown catalogue entry, assuming data corruption occurred, cannot read further the catalogue as I do not know the length of this type of entry")); return ret; // nullptr } } if(ret == nullptr && type != ' ') throw Ememory("cat_entree::read"); } catch(...) { if(read_crc) { crc * tmp = ptr->get_crc(); // keep pdesc in a coherent status if(tmp != nullptr) delete tmp; } throw; } if(read_crc) { crc *crc_calc = ptr->get_crc(); if(crc_calc == nullptr) throw SRC_BUG; if(type == ' ') // corrupted data while in lax mode { delete crc_calc; return ret; // returning nullptr } try { crc *crc_read = create_crc_from_file(*ptr); if(crc_read == nullptr) throw SRC_BUG; try { if(*crc_read != *crc_calc) { cat_nomme * ret_nom = dynamic_cast(ret); string nom = ret_nom != nullptr ? ret_nom->get_name() : ""; try { if(!lax) throw Erange("", "temporary exception"); else { if(nom == "") nom = gettext("unknown entry"); dialog->pause(tools_printf(gettext("Entry information CRC failure for %S. Ignore the failure?"), &nom)); } } catch(Egeneric & e) // we catch here the temporary exception and the Euser_abort thrown by dialog.pause() { if(nom != "") throw Erange("cat_entree::read", tools_printf(gettext("Entry information CRC failure for %S"), &nom)); else throw Erange("cat_entree::read", gettext(gettext("Entry information CRC failure"))); } } ret->post_constructor(*pdesc); } catch(...) { if(crc_read != nullptr) delete crc_read; throw; } if(crc_read != nullptr) delete crc_read; } catch(...) { if(crc_calc != nullptr) delete crc_calc; throw; } if(crc_calc != nullptr) delete crc_calc; } if(ret != nullptr) stats.add(ret); return ret; } cat_entree::cat_entree(const smart_pointer & x_pdesc, bool small, saved_status val): xsaved(val) { if(small && x_pdesc->esc == nullptr) throw SRC_BUG; change_location(x_pdesc); } void cat_entree::change_location(const smart_pointer & x_pdesc) { if(x_pdesc->stack == nullptr) throw SRC_BUG; if(x_pdesc->compr == nullptr) throw SRC_BUG; pdesc = x_pdesc; } void cat_entree::dump(const pile_descriptor & pdesc, bool small) const { pdesc.check(small); if(small) { crc *tmp = nullptr; try { pdesc.stack->sync_write_above(pdesc.esc); pdesc.esc->reset_crc(ENTREE_CRC_SIZE); try { inherited_dump(pdesc, small); } catch(...) { tmp = pdesc.esc->get_crc(); // keep f in a coherent status throw; } tmp = pdesc.esc->get_crc(); if(tmp == nullptr) throw SRC_BUG; tmp->dump(*pdesc.esc); } catch(...) { if(tmp != nullptr) delete tmp; throw; } if(tmp != nullptr) delete tmp; } else inherited_dump(pdesc, small); } void cat_entree::set_list_entry(const slice_layout *sly, bool fetch_ea, list_entry & ent) const { const cat_nomme *tmp_nom = dynamic_cast(this); const cat_inode *tmp_inode = dynamic_cast(this); const cat_file *tmp_file = dynamic_cast(this); const cat_lien *tmp_lien = dynamic_cast(this); const cat_device *tmp_device = dynamic_cast(this); const cat_mirage *tmp_mir = dynamic_cast(this); const cat_directory *tmp_dir = dynamic_cast(this); const cat_detruit *tmp_det = dynamic_cast(this); ent.clear(); if(tmp_mir == nullptr) { ent.set_hard_link(false); ent.set_type(signature()); } else { ent.set_hard_link(true); ent.set_type(tmp_mir->get_inode()->signature()); tmp_inode = tmp_mir->get_inode(); tmp_file = dynamic_cast(tmp_inode); tmp_lien = dynamic_cast(tmp_inode); tmp_device = dynamic_cast(tmp_inode); ent.set_etiquette(tmp_mir->get_etiquette()); } if(tmp_nom != nullptr) ent.set_name(tmp_nom->get_name()); if(tmp_det != nullptr) { ent.set_removed_type(tmp_det->get_signature()); ent.set_removal_date(tmp_det->get_date()); } if(tmp_inode != nullptr) { ent.set_uid(tmp_inode->get_uid()); ent.set_gid(tmp_inode->get_gid()); ent.set_perm(tmp_inode->get_perm()); ent.set_last_access(tmp_inode->get_last_access()); ent.set_last_modif(tmp_inode->get_last_modif()); ent.set_saved_status(tmp_inode->get_saved_status()); ent.set_ea_status(tmp_inode->ea_get_saved_status()); if(tmp_inode->has_last_change()) ent.set_last_change(tmp_inode->get_last_change()); if(tmp_inode->ea_get_saved_status() == ea_saved_status::full) { infinint tmp; if(tmp_inode->ea_get_offset(tmp)) ent.set_archive_offset_for_EA(tmp); ent.set_storage_size_for_EA(tmp_inode->ea_get_size()); if(fetch_ea) { const ea_attributs *not_owned = tmp_inode->get_ea(); if(not_owned == nullptr) throw SRC_BUG; ent.set_ea(*not_owned); } } ent.set_fsa_status(tmp_inode->fsa_get_saved_status()); if(tmp_inode->fsa_get_saved_status() == fsa_saved_status::full || tmp_inode->fsa_get_saved_status() == fsa_saved_status::partial) { ent.set_fsa_scope(tmp_inode->fsa_get_families()); if(tmp_inode->fsa_get_saved_status() == fsa_saved_status::full) { infinint tmp; if(tmp_inode->fsa_get_offset(tmp)) ent.set_archive_offset_for_FSA(tmp); ent.set_storage_size_for_FSA(tmp_inode->fsa_get_size()); } } } if(tmp_file != nullptr) { const crc *crc_tmp = nullptr; ent.set_file_size(tmp_file->get_size()); ent.set_is_sparse_file(tmp_file->get_sparse_file_detection_read()); ent.set_compression_algo(tmp_file->get_compression_algo_read()); ent.set_dirtiness(tmp_file->is_dirty()); if(tmp_file->get_saved_status() == saved_status::saved || tmp_file->get_saved_status() == saved_status::delta) { ent.set_archive_offset_for_data(tmp_file->get_offset()); ent.set_storage_size_for_data(tmp_file->get_storage_size()); if(tmp_file->get_crc(crc_tmp) && crc_tmp != nullptr) ent.set_data_crc(*crc_tmp); } if(tmp_file->has_delta_signature_structure()) { tmp_file->read_delta_signature_metadata(); tmp_file->drop_delta_signature_data(); } ent.set_delta_sig(tmp_file->has_delta_signature_available()); if(tmp_file->has_patch_base_crc() && tmp_file->get_patch_base_crc(crc_tmp) && crc_tmp != nullptr) ent.set_delta_patch_base_crc(*crc_tmp); if(tmp_file->has_patch_result_crc() && tmp_file->get_patch_result_crc(crc_tmp) && crc_tmp != nullptr) ent.set_delta_patch_result_crc(*crc_tmp); } if(tmp_dir != nullptr) { ent.set_file_size(tmp_dir->get_size()); ent.set_storage_size_for_data(tmp_dir->get_storage_size()); ent.set_empty_dir(tmp_dir->is_empty()); } if(tmp_lien != nullptr && tmp_lien->get_saved_status() == saved_status::saved) ent.set_link_target(tmp_lien->get_target()); if(tmp_device != nullptr && tmp_device->get_saved_status() == saved_status::saved) { ent.set_major(tmp_device->get_major()); ent.set_minor(tmp_device->get_minor()); } if(sly != nullptr && tmp_nom != nullptr) ent.set_slices(macro_tools_get_slices(tmp_nom, *sly)); } void cat_entree::inherited_dump(const pile_descriptor & pdesc, bool small) const { cat_signature s(signature(), get_saved_status()); pdesc.check(small); if(small) s.write(*(pdesc.esc)); else s.write(*(pdesc.stack)); } generic_file *cat_entree::get_read_cat_layer(bool small) const { generic_file *ret = nullptr; pdesc->check(small); if(small) { pdesc->stack->flush_read_above(pdesc->esc); ret = pdesc->esc; } else ret = pdesc->stack; return ret; } const char *cat_entree_signature2string(unsigned char sign) { unsigned char normalized_sig = toupper(sign); switch(normalized_sig) { case 'D': return gettext("directory"); case 'Z': throw SRC_BUG; // EOD should never be considered by overwriting policy case 'M': return gettext("hard linked inode"); case 'F': return gettext("plain file"); case 'L': return gettext("soft link"); case 'C': return gettext("char device"); case 'B': return gettext("block device"); case 'P': return gettext("named pipe"); case 'S': return gettext("unix socket"); case 'X': return gettext("deleted entry"); case 'O': return gettext("door inode"); case 'I': throw SRC_BUG; // ignored entry should never be found in an archive case 'J': throw SRC_BUG; // ignored directory entry should never be found in an archive default: throw SRC_BUG; // unknown entry type } } } // end of namespace dar-2.7.15/src/libdar/cat_lien.hpp0000644000175000017500000000574314636066467013643 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_lien.hpp /// \brief class used to store symbolic links in a catalogue /// \ingroup Private #ifndef CAT_LIEN_HPP #define CAT_LIEN_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_inode.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the symbolic link inode class class cat_lien : public cat_inode { public : cat_lien(const infinint & uid, const infinint & gid, U_16 perm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const std::string & name, const std::string & target, const infinint & fs_device); cat_lien(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small); cat_lien(const cat_lien & ref) = default; cat_lien(cat_lien && ref) noexcept = default; cat_lien & operator = (const cat_lien & ref) = default; cat_lien & operator = (cat_lien && ref) = default; ~cat_lien() = default; virtual bool operator == (const cat_entree & ref) const override; const std::string & get_target() const; void set_target(std::string x); // using the method is_more_recent_than() from cat_inode // using method has_changed_since() from cat_inode class /// inherited from cat_entree virtual unsigned char signature() const override { return 'l'; }; /// inherited from cat_entree virtual std::string get_description() const override { return "symlink"; }; /// inherited from cat_entree virtual cat_entree *clone() const override { return new (std::nothrow) cat_lien(*this); }; protected : virtual void sub_compare(const cat_inode & other, bool isolated_mode) const override; virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; private : std::string points_to; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/escape.hpp0000644000175000017500000002735614636067146013324 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file escape.hpp /// \brief class escape definition, used for sequential reading of archives /// \ingroup Private /// /// The class escape is used to insert escape sequences before each new file's /// data in an archive. The normal file's data is also rewritten if it contains /// such an escape sequence for it does not collide with real escape sequences /// At reading time, this class revert backs modification done to file's data /// containing escape sequences for they contain the original data. This class /// also provides the feature to skip to the next (real) escape sequence. /// This class inherits of generic files and its objects are to be used in a /// stack of generic file's objects. The object below contains modified data /// and escape sequences, the file over gets the normal file data and does /// never see escape sequences. Expected implementation is to have a compressor /// above an escape object and a sar or scrambler/blowfish/... object above it. #ifndef ESCAPE_HPP #define ESCAPE_HPP #include "../my_config.h" extern "C" { #if HAVE_LIMITS_H #include #endif } #include #include "generic_file.hpp" #define ESCAPE_FIXED_SEQUENCE_NORMAL 0xAD #define ESCAPE_FIXED_SEQUENCE_SPARSE_FILE 0xAE #define MAX_BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < MAX_BUFFER_SIZE #undef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE SSIZE_MAX #endif #endif namespace libdar { /// \addtogroup Private /// @{ class escape : public generic_file { public: enum sequence_type { seqt_undefined, ///< not enough data to define the type of the escape sequence seqt_not_a_sequence, ///< to escape data corresponding to an escape sequence's fixed byte sequence seqt_file, ///< placed before inode information, eventually followed by file data seqt_ea, ///< placed before EA data seqt_catalogue, ///< placed before the archive's internal catalogue seqt_data_name, ///< placed before the archive data_name (at the beginning of the archive) seqt_file_crc, ///< placed before the CRC of file's data seqt_ea_crc, ///< placed before the CRC of file's EA seqt_changed, ///< placed before new copy of file's data if file's data changed while reading it for backup seqt_dirty, ///< placed after data CRC if file is dirty seqt_failed_backup, ///< placed after inode information if the file could not be openned at backup time seqt_fsa, ///< placed before FSA data seqt_fsa_crc, ///< placed before the CRC of file's FSA seqt_delta_sig, ///< placed before the delta signature of a file seqt_in_place ///< placed before the in_place path at the beginning of the archive }; // the archive layout of marks is for each entry: // #seqt_file# [ [#seqt_changed# [...] ] #seqt_file_crc# [#seqt_dirty#]] [#seqt_ea# #seqt_ea_crc# ] // this previous sequence that we will call is repeated for each file, then on the overall archive we have : // #seqt_data_name# ... #seqt_catalogue# // the provided "below" object must exist during the whole live of the escape object, // the escape object does not own this "below" object // it must be destroyed by the caller/creator of the escape object. // constructors & destructors escape(generic_file *below, ///< "Below" is the generic file that holds the escaped data const std::set & x_unjumpable ///< a set of marks that can never been jumped over when skipping for the next mark of a any given type. ); escape(const escape & ref) : generic_file(ref) { copy_from(ref); }; escape(escape && ref) noexcept : generic_file(std::move(ref)) { nullifyptr(); move_from(std::move(ref)); }; escape & operator = (const escape & ref); escape & operator = (escape && ref) noexcept { generic_file::operator = (std::move(ref)); move_from(std::move(ref)); return *this; }; ~escape(); // escape specific routines void add_mark_at_current_position(sequence_type t); /// skip forward to the next mark of given type /// \param[in] t type of mark to skip to /// \param[in] jump if set to false, do not jump over *any* mark, even those not set as unjumpable mark, /// set it to true, to allow jumping over marks except those defined as unjumpable marks /// \return true if could skip to mark of type t bool skip_to_next_mark(sequence_type t, bool jump); bool next_to_read_is_mark(sequence_type t); bool next_to_read_is_which_mark(sequence_type & t); void add_unjumpable_mark(sequence_type t) { if(is_terminated()) throw SRC_BUG; unjumpable.insert(t); }; void remove_unjumpable_mark(sequence_type t); bool is_unjumpable_mark(sequence_type t) const { return unjumpable.find(t) != unjumpable.end(); }; void clear_all_unjumpable_marks() { unjumpable.clear(); }; // generic_file inherited routines // NOTA: Nothing is done to prevent skip* operation to put the read cursor in the middle of an escape sequence and // thus incorrectly consider it as normal data. Such event should only occure upon archive corruption and will be detected // by checksum mechanisms. virtual bool skippable(skippability direction, const infinint & amount) override; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return x_below->truncatable(pos); }; virtual infinint get_position() const override; protected: virtual void inherited_read_ahead(const infinint & amount) override; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override { flush_write(); }; virtual void inherited_flush_read() override { flush_write(); clean_read(); }; virtual void inherited_terminate() override { flush_or_clean(); }; void change_fixed_escape_sequence(unsigned char value) { fixed_sequence[0] = value; }; bool has_escaped_data_since_last_skip() const { return escaped_data_count_since_last_skip > 0; }; private: //-- constants /// total lenght of the escape sequence static constexpr U_I ESCAPE_SEQUENCE_LENGTH = 6; static constexpr U_I INITIAL_WRITE_BUFFER_SIZE = 2*ESCAPE_SEQUENCE_LENGTH; static constexpr U_I INITIAL_READ_BUFFER_SIZE = MAX_BUFFER_SIZE; static const infinint READ_BUFFER_SIZE_INFININT; /// escape sequence value /// an escape sequence starts by this sequence of characters. The last one is replaced by the /// type of sequence. The first one is fixed but may be set to another value using the protected method /// change_escape_sequence(). This opens the possibility to have several nested escape objects /// without having the bottom one escaping the escape sequence of the one above it. /// this constant table is defined in escape.cpp static const unsigned char usual_fixed_sequence[ESCAPE_SEQUENCE_LENGTH]; //-- variables generic_file *x_below; ///< the generic_file in which we read/write escaped data from/to the object is not owned by "this" U_I write_buffer_size; ///< amount of data in write transit not yet written to "below" (may have to be escaped) char write_buffer[INITIAL_WRITE_BUFFER_SIZE]; ///< data in write transit, all data is unescaped, up to the first real mark, after it, data is raw (may be escaped) ///< the first real mark is pointed to by escape_seq_offset_in_buffer U_I already_read; ///< data in buffer that has already been returned to the upper layer bool read_eof; ///< whether we reached a escape sequence while reading data U_I escape_seq_offset_in_buffer; ///< location of the first escape sequence which is not a data sequence char* read_buffer; ///< data in read transit U_I read_buffer_size; ///< amount of data in transit, read from below, but possibly not yet unescaped and returned to the upper layer U_I read_buffer_alloc; ///< allocated size of read_buffer std::set unjumpable; ///< list of mark that cannot be jumped over when searching for the next mark unsigned char fixed_sequence[ESCAPE_SEQUENCE_LENGTH]; ///< the preambule of an escape sequence to use/search for infinint escaped_data_count_since_last_skip; infinint below_position; ///< remember the position of object pointed to by x_below //-- routines void set_fixed_sequence_for(sequence_type t) { fixed_sequence[ESCAPE_SEQUENCE_LENGTH - 1] = type2char(t); }; void check_below() const { if(x_below == nullptr) throw SRC_BUG; }; void clean_read(); ///< drops all in-transit data void flush_write(); ///< write down to "below" all in-transit data void flush_or_clean() { switch(get_mode()) { case gf_read_only: clean_read(); break; case gf_write_only: case gf_read_write: flush_write(); break; default: throw SRC_BUG; } }; void nullifyptr() noexcept { x_below = nullptr; }; void copy_from(const escape & ref); void move_from(escape && ref) noexcept; bool mini_read_buffer(); ///< returns true if it could end having at least ESCAPE_SEQUENCE_LENGTH bytes in read_buffer, false else (EOF reached). //-- static routine(s) // some convertion routines static char type2char(sequence_type x); static sequence_type char2type(char x); /// unescape data from data marks, up to the first real escape sequence found /// find the next start of escape sequence in the given buffer /// \return the offset of the first start of escape sequence (ESCAPE_SEQUENCE_LENGTH - 1), /// or partial if found at the end, returns size if none could be found static U_I trouve_amorce(const char *a, U_I size, const unsigned char escape_sequence[ESCAPE_SEQUENCE_LENGTH]); /// unescape data from data marks, up to the first real escape sequence found /// \param[in] a buffer to read data from /// \param[in] size the amount of data in "a" /// \param[out] delta the number of byte / the number of data escape sequence removed /// \param[in] escape_sequence the escape sequence start to look for /// return the offset of the real sequence and updates the size of the buffer /// if some data mark have been removed (size gets smaller) static U_I remove_data_marks_and_stop_at_first_real_mark(char *a, U_I size, U_I & delta, const unsigned char escape_sequence[ESCAPE_SEQUENCE_LENGTH]); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_delta_signature.hpp0000644000175000017500000002417214636066467016063 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_delta_signature.hpp /// \brief class used to manage binary delta signature in catalogue and archive /// \ingroup Private #ifndef CAT_DELTA_SIGNATURE_HPP #define CAT_DELTA_SIGNATURE_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "memory_file.hpp" #include "crc.hpp" #include "proto_compressor.hpp" #include "archive_version.hpp" #include namespace libdar { /// \addtogroup Private /// @{ // Datastructure in archive (DATA+METADATA) // // SEQUENTIAL MODE - in the core of the archive // +------+------+-----------+--------------+----------+--------+ // | base | sig | sig block |sig data | data CRC | result | // | CRC | size | len (if |(if size > 0) | if | CRC | // | (*) | | size > 0) | | size > 0 | | // +------+------+-----------+--------------+----------+--------+ // // DIRECT MODE - in catalogue at end of archive (METADATA) // +------+------+---------------+--------+ // | base | sig | sig offset | result | // | CRC | size | (if size > 0) | CRC | // | (*) | | | | // +------+------+---------------+--------+ // // DIRECT MODE - in the core of the archive (DATA) // +-----------+---------------+----------+ // | sig block | sig data | data CRC | // | len (if | (if size > 0) | if | // | size > 0) | | size > 0 | // +-----------+---------------+----------+ // // (*) base_CRC has been moved to cat_file structure since format 11,2 // in order to read this field before the delta patch in sequential read mode // // this structure is used for all cat_file inode that have // either a delta signature or contain a delta patch (s_delta status) // base_crc is used to check we apply the patch on the good file before proceeding // result_crc is used to store the crc of the file the signature has been computed on // result_crc is also used once a patch has been applied to verify the correctness of the patch result /// the cat_delta_signature file class /// this class works in two implicit modes /// - read mode /// read the metadata from an archive the caller having to know where to find it /// read the data and fill the provided memory_file (get_sig()) by the delta signature /// provide access to the associated CRC /// - write mode /// stores the associated CRC /// write down the given delta signature (with metadata in sequential mode) /// write down the metadata /// the signature is not stored class cat_delta_signature { public: /// constructor to read an object (using read() later on) from filesystem/backup /// \param[in] f where to read the data from, used when calling read() later on. f must not /// be set to nullptr and the pointed to object must survive this cat_delta_signature object /// \param[in] c points to the compressor layer in order to suspend compression when reading /// data (metadata will be read compressed or not depending on the its location (in-lined or /// in the catalogue at end of archive) cat_delta_signature(generic_file *f, proto_compressor* c); /// constructor to write an object to filesytem/backup (using dump_* methods later on) cat_delta_signature() { init(); }; /// copy constructor cat_delta_signature(const cat_delta_signature & ref) { init(); copy_from(ref); }; /// move constructor cat_delta_signature(cat_delta_signature && ref) noexcept { init(); move_from(std::move(ref)); }; /// assignement operator cat_delta_signature & operator = (const cat_delta_signature & ref) { clear(); copy_from(ref); return *this; }; /// move assignment operator cat_delta_signature & operator = (cat_delta_signature && ref) noexcept { move_from(std::move(ref)); return *this; }; /// destructor ~cat_delta_signature() { destroy(); }; /////////// method for read mode /////////// /// tells whether the read() call has been invoked bool is_pending_read() const { return pending_read; }; /// read the metadata of the object from the generic_file given at construction time /// \note in sequential read mode, the data is also read at that time and loaded into memory, /// thing which is done transparently by obtain_sig() when in direct access mode void read(bool sequential_read, const archive_version & ver); /// the cat_delta_signature structure can only hold CRC without delta_signature, this call gives the situation about that point bool can_obtain_sig() const { return !delta_sig_size.is_zero(); }; /// provide a memory_file object which the caller has the duty to destroy after use /// \note while drop_sig has not been called, obtain_sig() can be called any number of time /// \note in direct mode (not sequential_real mode) the first call to obtain_sig() fetches /// the data from the archive and loads it to memory. std::shared_ptr obtain_sig(const archive_version & ver) const; /// provide the block size used for delta signature U_I obtain_sig_block_size() const { return sig_block_len; }; /// drop signature but keep metadata available /// \note there is a lot of chance that a call to obtain_sig() will fail after drop_sig() has been /// called when in sequential read mode, due to the limited possibility to skip backward in that mode void drop_sig() const { sig.reset(); }; /////////// method for write mode /////////// /// give the object where to fetch from the delta signature, object must exist up to the next call to dump_data /// \note seg_sig_ref() must be called each time before invoking dump_data(), normally it is done once... /// for can_obtain_sig() to return true before the signature is provided void will_have_signature() { delta_sig_size = 1; }; /// the object pointed to by ptr must stay available when calling dump_data()/dump_metadata() later on /// \note sig_block_size is an additional information about the block size used to setup the signature, /// this is not the size of the signature! void set_sig(const std::shared_ptr & ptr, U_I sig_block_size); /// variante used when the delta_signature object will only contain CRCs (no delta signature) void set_sig() { delta_sig_size = 0; delta_sig_offset = 0; sig_block_len = 0; sig.reset(); }; /// write down the data eventually with sequential read mark followed by delta sig metadata /// \note ver is only used to know which version to use for reading the data, but it is /// always written following the most recent supported archive format version void dump_data(generic_file & f, bool sequential_mode, const archive_version & ver) const; /// write down the delta_signature metadata for catalogue void dump_metadata(generic_file & f) const; /////////// method for both read and write modes /////////// /// returns whether the object has a base patch CRC (s_delta status objects) bool has_patch_base_crc() const { return patch_base_check != nullptr; }; /// returns the CRC of the file to base the patch on, for s_delta objects bool get_patch_base_crc(const crc * & c) const; /// set the reference CRC of the file to base the patch on, for s_detla objects void set_patch_base_crc(const crc & c); /// returns whether the object has a CRC corresponding to data (for s_saved, s_delta, and when delta signature is present) bool has_patch_result_crc() const { return patch_result_check != nullptr; }; /// returns the CRC the file will have once restored or patched (for s_saved, s_delta, and when delta signature is present) bool get_patch_result_crc(const crc * & c) const; /// set the CRC the file will have once restored or patched (for s_saved, s_delta, and when delta signature is present) void set_patch_result_crc(const crc & c); /// reset the object void clear() { destroy(); init(); }; private: crc *patch_base_check; ///< associated CRC for the file this signature has been computed on, moved to cat_file since format 11.2, still need for older formats infinint delta_sig_size; ///< size of the data to setup "sig" (set to zero when reading in sequential mode, sig is then setup on-fly) infinint delta_sig_offset; ///< where to read sig_block_len followed by delta_sig_size bytes of data from which to setup "sig" ///\note delta_sig_offset is set to zero when read in sequential mode, sig is setup on-fly mutable std::shared_ptrsig; ///< the signature data, if set nullptr it will be fetched from f in direct access mode only crc *patch_result_check; ///< associated CRC generic_file *src; ///< where to read data from proto_compressor *zip; ///< needed to disable compression when reading delta signature data from an archive mutable U_I sig_block_len; ///< block length used within delta signature bool pending_read; ///< when the object has been created for read but data not yet read from archive void init() noexcept; void copy_from(const cat_delta_signature & ref); void move_from(cat_delta_signature && ref) noexcept; void destroy() noexcept; // reads sig_block_len + sig data void fetch_data(const archive_version & ver) const; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/shell_interaction.cpp0000644000175000017500000007245014636067146015560 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_STDIO_H #include #endif #if HAVE_LIMITS_H #include #endif #if HAVE_ERRNO_H #include #endif } // end extern "C" // L_ctermid should be defined from stdio.h but this is not // the case under cygwin! So we define it ourself with a // somehow arbitrarily large value taking the risk it to be // too short and having ctermid() writing over the reserved buffer // space #ifndef L_ctermid #define L_ctermid 200 #endif #include "tools.hpp" #include "integers.hpp" #include "cygwin_adapt.hpp" #include "shell_interaction.hpp" #include "nls_swap.hpp" #ifndef L_ctermid // cygwin does not always define L_ctermid in stdio.h #define L_ctermid 10240 // this value should be large enough for most cases // if it is smaller than system expects, ctermid() may lead // to program crash #endif using namespace std; using namespace libdar; namespace libdar { #ifdef SSIZE_MAX const U_I shell_interaction::bufsize = 1024 > SSIZE_MAX ? SSIZE_MAX : 1024; #else const U_I shell_interaction::bufsize = 1024; #endif shell_interaction::shell_interaction(ostream & out, ostream & interact, bool silent): output(&out), inter(&interact) { NLS_SWAP_IN; try { has_terminal = false; beep = false; at_once = 0; count = 0; // looking for an input terminal // // we do not use anymore standart input but open a new descriptor // from the controlling terminal. This allow in some case to keep use // standart input for piping data while still having user interaction // possible. // terminal settings try { char tty[L_ctermid+1]; struct termios term; (void)ctermid(tty); tty[L_ctermid] = '\0'; input = ::open(tty, O_RDONLY|O_TEXT); if(input < 0) throw Erange("",""); // used locally else { if(silent) has_terminal = false; // force non interactive mode else // preparing input for swaping between char mode and line mode (terminal settings) if(tcgetattr(input, &term) >= 0) { initial = term; initial_noecho = term; initial_noecho.c_lflag &= ~ECHO; term.c_lflag &= ~ICANON; term.c_lflag &= ~ECHO; term.c_cc[VTIME] = 0; term.c_cc[VMIN] = 1; interaction = term; // checking now that we can change to character mode set_term_mod(m_inter); set_term_mod(m_initial); // but we don't need it right now, so swapping back to line mode has_terminal = true; } else // failed to retrieve parameters from tty throw Erange("",""); // used locally } } catch(Erange & e) { if(e.get_message() == "") { if(!silent) message(gettext("No terminal found for user interaction. All questions will be assumed a negative answer (less destructive choice), which most of the time will abort the program.")); } else throw; } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } /// copy constructor shell_interaction::shell_interaction(const shell_interaction & ref): user_interaction(ref), output(ref.output), inter(ref.inter) { if(ref.input >= 0) { input = dup(ref.input); if(input < 0) throw Erange("shell_interaction::shell_interaction", string("Failed dup()-licating file descriptor: ") + tools_strerror_r(errno)); } else input = ref.input; beep = ref.beep; initial = ref.initial; interaction = ref.interaction; initial_noecho = ref.initial_noecho; has_terminal = ref.has_terminal; } shell_interaction::~shell_interaction() { if(has_terminal) set_term_mod(m_initial); if(input >= 0) { close(input); input = -1; } } void shell_interaction::change_non_interactive_output(ostream & out) { output = &out; } void shell_interaction::read_char(char & a) { NLS_SWAP_IN; try { sigset_t old_mask; if(input < 0) throw SRC_BUG; tools_block_all_signals(old_mask); set_term_mod(m_inter); if(read(input, &a, 1) < 0) throw Erange("shell_interaction_read_char", string(gettext("Error reading character: ")) + strerror(errno)); tools_blocking_read(input, true); set_term_mod(m_initial); tools_set_back_blocked_signals(old_mask); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void shell_interaction::archive_show_contents(const archive & ref, const archive_options_listing_shell & options) { NLS_SWAP_IN; try { archive_listing_sizes_in_bytes = options.get_sizes_in_bytes(); archive_listing_display_ea = options.get_display_ea(); all_slices.clear(); marge = ""; switch(options.get_list_mode()) { case archive_options_listing_shell::normal: printf(gettext("[Data ][D][ EA ][FSA][Compr][S]| Permission | User | Group | Size | Date | filename")); printf( "--------------------------------+------------+-------+-------+---------+-------------------------------+------------"); ref.op_listing(archive_listing_callback_tar, this, options); break; case archive_options_listing_shell::tree: printf(gettext("Access mode | User | Group | Size | Date |[Data ][D][ EA ][FSA][Compr][S]| Filename")); printf( "---------------+------+-------+--------+-------------------------------+--------------------------------+-----------"); ref.op_listing(archive_listing_callback_tree, this, options); break; case archive_options_listing_shell::xml: message(""); message(""); message(""); ref.op_listing(archive_listing_callback_xml, this, options); message(""); break; case archive_options_listing_shell::slicing: message("Slice(s)|[Data ][D][ EA ][FSA][Compr][S]|Permission| Filemane"); message("--------+--------------------------------+----------+-----------------------------"); ref.op_listing(archive_listing_callback_slicing, this, options); message("-----"); message(tools_printf("All displayed files have their data in slice range [%s]", all_slices.display().c_str())); message("-----"); break; default: throw SRC_BUG; } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void shell_interaction::database_show_contents(const database & ref) { NLS_SWAP_IN; try { database_archives_list content = ref.get_contents(); string opt = tools_concat_vector(" ", ref.get_options()); string road, base; string compr = compression2string(ref.get_compression()); U_I compr_lvl = ref.get_compression_level(); string dar_path = ref.get_dar_path(); string db_version = ref.get_database_version(); message(""); printf(gettext("dar path : %S"), &dar_path); printf(gettext("dar options : %S"), &opt); printf(gettext("database version : %S"), &db_version); printf(gettext("compression used : %S"), &compr); printf(gettext("compression level: %d"), compr_lvl); message(""); printf(gettext("archive # | path | basename")); printf("------------+--------------+---------------"); for(archive_num i = 1; i < content.size(); ++i) { road = content[i].get_path(); base = content[i].get_basename(); opt = (road == "") ? gettext("") : road; printf(" \t%u\t%S\t%S", i, &opt, &base); } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void shell_interaction::database_show_files(const database & ref, archive_num num, const database_used_options & opt) { NLS_SWAP_IN; try { ref.get_files(show_files_callback, this, num, opt); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void shell_interaction::database_show_version(const database & ref, const path & chemin) { NLS_SWAP_IN; try { ref.get_version(get_version_callback, this, chemin); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void shell_interaction::database_show_statistics(const database & ref) { NLS_SWAP_IN; try { printf(gettext(" archive # | most recent/total data | most recent/total EA")); printf(gettext("--------------+-------------------------+-----------------------")); // having it with gettext let the translater adjust columns width ref.show_most_recent_stats(statistics_callback, this); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void shell_interaction::inherited_message(const string & message) { if(at_once > 0) { U_I c = 0, max = message.size(); while(c < max) { if(message[c] == '\n') count++; c++; } count++; // for the implicit \n at end of message if(count >= at_once) { count = 0; pause(libdar::dar_gettext("Continue? ")); } } my_message(message); } bool shell_interaction::inherited_pause(const string &message) { char buffer[bufsize]; char & a = buffer[0]; char & b = buffer[1]; bool ret; if(!has_terminal) return false; if(input < 0) throw SRC_BUG; set_term_mod(shell_interaction::m_inter); try { sigset_t old_mask; S_I tmp_ret, errno_bk, tmp_sup, errno_sup; do { // flushing any character remaining in the input stream tools_blocking_read(input, false); while(read(input, buffer, bufsize) >= 0) ; tools_blocking_read(input, true); // now asking the user *(inter) << message << gettext(" [return = YES | Esc = NO]") << (beep ? "\007\007\007" : "") << endl; tools_block_all_signals(old_mask); tmp_ret = read(input, &a, 1); errno_bk = errno; // checking if another character is available in the pipe tools_blocking_read(input, false); errno_sup = EAGAIN+1; // = something different from EAGAIN, whatever it is... usleep(10000); // let a change for any other typed character to reach the input device tmp_sup = read(input, &b, 1); errno_sup = errno; tools_blocking_read(input, true); // checking error conditions tools_set_back_blocked_signals(old_mask); if(tmp_ret < 0) if(errno_bk != EINTR) throw Erange("shell_interaction:interaction_pause", string(gettext("Error while reading user answer from terminal: ")) + strerror(errno_bk)); } while((a != 27 && a != '\n') || tmp_sup != -1 || errno_sup != EAGAIN); if(a != 27) *(inter) << gettext("Continuing...") << endl; else *(inter) << gettext("Escaping...") << endl; ret = a != 27; // 27 is escape key } catch(...) { set_term_mod(shell_interaction::m_initial); throw; } set_term_mod(shell_interaction::m_initial); return ret; } string shell_interaction::inherited_get_string(const string & message, bool echo) { string ret; const U_I expected_taille = 100; #ifdef SSIZE_MAX const U_I taille = expected_taille > SSIZE_MAX ? SSIZE_MAX : expected_taille; #else const U_I taille = expected_taille; #endif U_I lu, i; char buffer[taille+1]; bool fin = false; if(!echo) set_term_mod(shell_interaction::m_initial); if(output == nullptr || input < 0) throw SRC_BUG; // shell_interaction has not been properly initialized *(inter) << message; do { lu = ::read(input, buffer, taille); i = 0; while(i < lu && buffer[i] != '\n') ++i; if(i < lu) fin = true; buffer[i] = '\0'; ret += string(buffer); } while(!fin); if(!echo) *(inter) << endl; set_term_mod(shell_interaction::m_initial); return ret; } secu_string shell_interaction::inherited_get_secu_string(const string & message, bool echo) { const U_I expected_taille = 1000; #ifdef SSIZE_MAX const U_I taille = expected_taille > SSIZE_MAX ? SSIZE_MAX : expected_taille; #else const U_I taille = expected_taille; #endif secu_string ret = taille; bool fin = false; U_I last = 0, i = 0; if(!has_terminal) throw Erange("shell_interaction::interaction_secu_string", gettext("Secured string can only be read from a terminal")); if(!echo) set_term_mod(shell_interaction::m_noecho); try { if(output == nullptr || input < 0) throw SRC_BUG; // shell_interaction has not been properly initialized *(inter) << message; do { ret.append(input, taille - ret.get_size()); i = last; while(i < ret.get_size() && ret.c_str()[i] != '\n') ++i; if(i < ret.get_size()) // '\n' found so we stop here but remove this last char { fin = true; ret.reduce_string_size_to(i); } else last = i; if(ret.get_size() == taille && !fin) throw Erange("interaction_secu_string", gettext("provided password is too long for the allocated memory")); } while(!fin); if(!echo) *(inter) << endl; } catch(...) { set_term_mod(shell_interaction::m_initial); throw; } set_term_mod(shell_interaction::m_initial); return ret; } void shell_interaction::set_term_mod(shell_interaction::mode m) { termios *ptr = nullptr; switch(m) { case m_initial: ptr = &initial; break; case m_inter: ptr = &interaction; break; case m_noecho: ptr = &initial_noecho; break; default: throw SRC_BUG; } if(tcsetattr(input, TCSANOW, ptr) < 0) throw Erange("shell_interaction : set_term_mod", string(gettext("Error while changing user terminal properties: ")) + strerror(errno)); } void shell_interaction::my_message(const std::string & mesg) { if(output == nullptr) throw SRC_BUG; // shell_interaction has not been properly initialized *output << mesg; if(mesg.rbegin() == mesg.rend() || *(mesg.rbegin()) != '\n') *output << endl; } void shell_interaction::archive_listing_callback_tree(const std::string & the_path, const list_entry & entry, void *context) { static const string marge_plus = " | "; static const U_I marge_plus_length = marge_plus.size(); shell_interaction *me = (shell_interaction *)(context); if(me == nullptr) throw SRC_BUG; if(entry.is_eod()) { U_I length = me->marge.size(); if(length >= marge_plus_length) me->marge.erase(length - marge_plus_length, marge_plus_length); else throw SRC_BUG; me->printf("%S +---", &(me->marge)); } else { string nom = entry.get_name(); if(entry.is_removed_entry()) { string tmp_date = entry.get_removal_date(); char type = tools_cast_type_to_unix_type(entry.get_removed_type()); me->message(tools_printf(gettext("%S [%c] [ REMOVED ENTRY ] (%S) %S"), &me->marge, type, &tmp_date, &nom)); } else // not a removed entry { string a = entry.get_perm(); string b = entry.get_uid(true); string c = entry.get_gid(true); string d = entry.get_file_size(me->archive_listing_sizes_in_bytes); string e = entry.get_last_modif(); string f = entry.get_data_flag() + entry.get_delta_flag() + entry.get_ea_flag() + entry.get_fsa_flag() + entry.get_compression_ratio_flag() + entry.get_sparse_flag(); if(me->archive_listing_display_ea && entry.is_hard_linked()) { string tiq = entry.get_etiquette(); nom += tools_printf(" [%S] ", &tiq); } me->printf("%S%S\t%S\t%S\t%S\t%S\t%S %S", &me->marge, &a, &b, &c, &d, &e, &f, &nom); if(me->archive_listing_display_ea) { string key; entry.get_ea_reset_read(); while(entry.get_ea_read_next(key)) me->message(me->marge + gettext(" Extended Attribute: [") + key + "]"); } if(entry.is_dir()) me->marge += marge_plus; } } } void shell_interaction::archive_listing_callback_tar(const std::string & the_path, const list_entry & entry, void *context) { shell_interaction *me = (shell_interaction *)(context); if(me == nullptr) throw SRC_BUG; if(entry.is_eod()) return; if(entry.is_removed_entry()) { string tmp_date = entry.get_removal_date(); char type = tools_cast_type_to_unix_type(entry.get_removed_type()); me->printf("%s (%S) [%c] %S", gettext(REMOVE_TAG), &tmp_date, type, &the_path); } else { string a = entry.get_perm(); string b = entry.get_uid(true); string c = entry.get_gid(true); string d = entry.get_file_size(me->archive_listing_sizes_in_bytes); string e = entry.get_last_modif(); string f = entry.get_data_flag() + entry.get_delta_flag() + entry.get_ea_flag() + entry.get_fsa_flag() + entry.get_compression_ratio_flag() + entry.get_sparse_flag(); string tiq = ""; if(me->archive_listing_display_ea && entry.is_hard_linked()) tiq = tools_printf(" [%s]", entry.get_etiquette().c_str()); me->printf("%S %S %S\t%S\t%S\t%S\t%S%S", &f, &a, &b, &c, &d, &e, &the_path, &tiq); if(me->archive_listing_display_ea) { string key; entry.get_ea_reset_read(); while(entry.get_ea_read_next(key)) me->message(gettext(" Extended Attribute: [") + key + "]"); } } } void shell_interaction::archive_listing_callback_xml(const std::string & the_path, const list_entry & entry, void *context) { shell_interaction *me = (shell_interaction *)(context); if(me == nullptr) throw SRC_BUG; if(entry.is_eod()) { U_I length = me->marge.size(); if(length > 0) me->marge.erase(length - 1, 1); // removing the last tab character else throw SRC_BUG; me->printf("%S", &me->marge); } else { string name = tools_output2xml(entry.get_name()); unsigned char sig = entry.get_type(); if(entry.is_removed_entry()) { sig = entry.get_removed_type(); switch(sig) { case 'd': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 'f': case 'h': case 'e': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 'l': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 'c': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 'b': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 'p': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 's': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; default: throw SRC_BUG; } } else // other than cat_detruit object { string maj, min, chksum, chkbasesum, chkresultsum, target, delta_sig; string dirty, sparse; string size = entry.get_file_size(me->archive_listing_sizes_in_bytes); string stored = entry.get_storage_size_for_data(me->archive_listing_sizes_in_bytes); string crc_tmp; string crc_patch_base; string crc_patch_result; if(!entry.is_file() || !entry.is_sparse()) { infinint stor; entry.get_storage_size_for_data(stor); if(stor.is_zero()) stored = size; } // building entry for each type of cat_inode switch(sig) { case 'd': // directories me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->marge += "\t"; break; case 'h': // hard linked files case 'e': // hard linked files throw SRC_BUG; // no more used in dynamic data case 'f': // plain files dirty = yes_no(entry.is_dirty()); sparse = yes_no(entry.is_sparse()); if(!entry.has_data_present_in_the_archive()) stored = ""; chksum = entry.get_data_crc(); delta_sig = yes_no(entry.has_delta_signature()); chkbasesum = entry.get_delta_patch_base_crc(); chkresultsum = entry.get_delta_patch_result_crc(); me->printf("%S", &(me->marge), &name, &size, &stored, &chksum, &dirty, &sparse, &delta_sig, &chkbasesum, &chkresultsum); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 'l': // soft link target = tools_output2xml(entry.get_link_target()); me->printf("%S", &(me->marge), &name, &target); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 'c': case 'b': // this is maybe less performant, to have both 'c' and 'b' here and // make an additional test, but this has the advantage to not duplicate // very similar code, which would obviously evoluate the same way. // Experience shows that two identical codes even when driven by the same need // are an important source of bugs, as one may forget to update both of them, the // same way... if(sig == 'c') target = "character"; else target = "block"; // we re-used target variable which is not used for the current cat_inode if(entry.has_data_present_in_the_archive()) { maj = entry.get_major(); min = entry.get_minor(); } else maj = min = ""; me->printf("%S", &(me->marge), &name, &target, &maj, &min); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 'p': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; case 's': me->printf("%S", &(me->marge), &name); me->xml_listing_attributes(entry); me->printf("%S", &(me->marge)); break; default: throw SRC_BUG; } } } } void shell_interaction::archive_listing_callback_slicing(const std::string & the_path, const list_entry & entry, void *context) { shell_interaction *me = (shell_interaction *)(context); if(me == nullptr) throw SRC_BUG; if(entry.is_eod()) return; me->all_slices += entry.get_slices(); if(entry.is_removed_entry()) me->message(tools_printf("%s\t %s%S", entry.get_slices().display().c_str(), gettext(REMOVE_TAG), &the_path)); else { string a = entry.get_perm(); string f = entry.get_data_flag() + entry.get_delta_flag() + entry.get_ea_flag() + entry.get_fsa_flag() + entry.get_compression_ratio_flag() + entry.get_sparse_flag(); me->printf("%s\t %S%S %S", entry.get_slices().display().c_str(), &f, &a, &the_path); } } void shell_interaction::show_files_callback(void *tag, const std::string & filename, bool available_data, bool available_ea) { shell_interaction *dialog = (shell_interaction *)(tag); string etat = ""; if(dialog == nullptr) throw SRC_BUG; if(available_data) etat += gettext("[ Saved ]"); else etat += gettext("[ ]"); if(available_ea) etat += gettext("[ EA ]"); else etat += gettext("[ ]"); dialog->printf("%S %S", &etat, &filename); } void shell_interaction::get_version_callback(void *tag, archive_num num, db_etat data_presence, bool has_data_date, datetime data, db_etat ea_presence, bool has_ea_date, datetime ea) { const string REMOVED = gettext("removed "); const string PRESENT = gettext("present "); const string SAVED = gettext("saved "); const string ABSENT = gettext("absent "); const string PATCH = gettext("patch "); const string BROKEN = gettext("BROKEN "); const string INODE = gettext("inode "); const string NO_DATE = " "; string data_state; string ea_state; string data_date; string ea_date; shell_interaction *dialog = (shell_interaction *)(tag); if(dialog == nullptr) throw SRC_BUG; switch(data_presence) { case db_etat::et_saved: data_state = SAVED; break; case db_etat::et_patch: data_state = PATCH; break; case db_etat::et_patch_unusable: data_state = BROKEN; break; case db_etat::et_inode: data_state = INODE; break; case db_etat::et_present: data_state = PRESENT; break; case db_etat::et_removed: data_state = REMOVED; break; case db_etat::et_absent: data_state = ABSENT; break; default: throw SRC_BUG; } switch(ea_presence) { case db_etat::et_saved: ea_state = SAVED; break; case db_etat::et_present: ea_state = PRESENT; break; case db_etat::et_removed: ea_state = REMOVED; break; case db_etat::et_absent: ea_state = ABSENT; break; case db_etat::et_patch: throw SRC_BUG; case db_etat::et_patch_unusable: throw SRC_BUG; default: throw SRC_BUG; } if(!has_data_date) { data_state = ABSENT; data_date = NO_DATE; } else data_date = tools_display_date(data); if(!has_ea_date) { ea_state = ABSENT; ea_date = NO_DATE; } else ea_date = tools_display_date(ea); dialog->printf(" \t%u\t%S %S %S %S", num, &data_date, &data_state, &ea_date, &ea_state); } void shell_interaction::statistics_callback(void *tag, U_I number, const infinint & data_count, const infinint & total_data, const infinint & ea_count, const infinint & total_ea) { shell_interaction *dialog = (shell_interaction *)(tag); if(dialog == nullptr) throw SRC_BUG; dialog->printf("\t%u %i/%i \t\t\t %i/%i", number, &data_count, &total_data, &ea_count, &total_ea); } void shell_interaction::xml_listing_attributes(const list_entry & entry) { string user = entry.get_uid(true); string group = entry.get_gid(true); string permissions = entry.get_perm(); string atime = tools_uint2str(entry.get_last_access_s()); string mtime = tools_uint2str(entry.get_last_modif_s()); string ctime = tools_uint2str(entry.get_last_change_s()); string data; string metadata; // defining "data" string switch(entry.get_data_status()) { case saved_status::saved: data = "saved"; break; case saved_status::fake: case saved_status::not_saved: data = "referenced"; break; case saved_status::delta: data = "patch"; break; case saved_status::inode_only: data = "inode-only"; break; default: throw SRC_BUG; } // defining "metadata" string switch(entry.get_ea_status()) { case ea_saved_status::full: metadata = "saved"; break; case ea_saved_status::partial: case ea_saved_status::fake: metadata = "referenced"; break; case ea_saved_status::none: case ea_saved_status::removed: metadata = "absent"; break; default: throw SRC_BUG; } if(entry.is_removed_entry()) { data = "deleted"; metadata = "absent"; user = ""; group = ""; permissions = ""; atime = ""; ctime = ""; mtime = tools_uint2str(entry.get_removal_date_s()); } bool go_ea = archive_listing_display_ea && entry.has_EA_saved_in_the_archive() && !entry.is_removed_entry(); string end_tag = go_ea ? ">" : " />"; printf("%S"); printf("%S", &marge); } } } // end of namespace dar-2.7.15/src/libdar/entree_stats.cpp0000644000175000017500000001363714636066467014561 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "entree_stats.hpp" #include "cat_all_entrees.hpp" using namespace std; namespace libdar { void entree_stats::add(const cat_entree *ref) { if(dynamic_cast(ref) == nullptr // we ignore cat_eod && dynamic_cast(ref) == nullptr // as well we ignore "cat_ignored" && dynamic_cast(ref) == nullptr) // and "cat_ignored_dir" { const cat_inode *ino = dynamic_cast(ref); const cat_mirage *h = dynamic_cast(ref); const cat_detruit *x = dynamic_cast(ref); if(h != nullptr) // won't count twice the same inode if it is referenced with hard_link { ++num_hard_link_entries; if(!h->is_inode_counted()) { ++num_hard_linked_inodes; h->set_inode_counted(true); ino = h->get_inode(); } } if(ino != nullptr) { ++total; switch(ino->get_saved_status()) { case saved_status::saved: ++saved; break; case saved_status::inode_only: ++inode_only; break; case saved_status::fake: case saved_status::not_saved: break; case saved_status::delta: ++patched; break; default: throw SRC_BUG; } } if(x != nullptr) ++num_x; else { const cat_directory *d = dynamic_cast(ino); if(d != nullptr) ++num_d; else { const cat_chardev *c = dynamic_cast(ino); if(c != nullptr) ++num_c; else { const cat_blockdev *b = dynamic_cast(ino); if(b != nullptr) ++num_b; else { const cat_tube *p = dynamic_cast(ino); if(p != nullptr) ++num_p; else { const cat_prise *s = dynamic_cast(ino); if(s != nullptr) ++num_s; else { const cat_lien *l = dynamic_cast(ino); if(l != nullptr) ++num_l; else { const cat_door *D = dynamic_cast(ino); if(D != nullptr) ++num_D; else { const cat_file *f = dynamic_cast(ino); if(f != nullptr) ++num_f; else if(h == nullptr) throw SRC_BUG; // unknown entry } } } } } } } } } } void entree_stats::listing(user_interaction & dialog) const { dialog.printf(""); dialog.printf(gettext("CATALOGUE CONTENTS :")); dialog.printf(""); dialog.printf(gettext("total number of inode : %i"), &total); dialog.printf(gettext("fully saved : %i"), &saved); dialog.printf(gettext("binay delta patch : %i"), &patched); dialog.printf(gettext("inode metadata only : %i"), &inode_only); dialog.printf(gettext("distribution of inode(s)")); dialog.printf(gettext(" - directories : %i"), &num_d); dialog.printf(gettext(" - plain files : %i"), &num_f); dialog.printf(gettext(" - symbolic links : %i"), &num_l); dialog.printf(gettext(" - named pipes : %i"), &num_p); dialog.printf(gettext(" - unix sockets : %i"), &num_s); dialog.printf(gettext(" - character devices : %i"), &num_c); dialog.printf(gettext(" - block devices : %i"), &num_b); dialog.printf(gettext(" - Door entries : %i"), &num_D); dialog.printf(gettext("hard links information")); dialog.printf(gettext(" - number of inode with hard link : %i"), &num_hard_linked_inodes); dialog.printf(gettext(" - number of reference to hard linked inodes: %i"), &num_hard_link_entries); dialog.printf(gettext("destroyed entries information")); dialog.printf(gettext(" %i file(s) have been record as destroyed since backup of reference"), &num_x); dialog.printf(""); } } // end of namespace dar-2.7.15/src/libdar/tlv.hpp0000644000175000017500000000443514636066467012667 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file tlv.hpp /// \brief Generic Type Length Value data structures /// \ingroup Private #ifndef TLV_HPP #define TLV_HPP #include "memory_file.hpp" namespace libdar { /// \addtogroup Private /// @{ /// Type Length Value Structure /// /// this structure holds arbitrary type of data /// this is used in particular for the slice header /// \note a tlv is a memory_file, that way it holds the /// *V*alue and *L*ength of the data. Only the *T*ype field needs /// to be added to the memory_file datastructure class tlv : public memory_file { public: // constructors & Co. tlv() { type = 0; }; tlv(generic_file & f) { init(f); }; tlv(const tlv & ref) = default; tlv(tlv && ref) noexcept = default; tlv & operator = (const tlv & ref) = default; tlv & operator = (tlv && ref) noexcept = default; ~tlv() = default; // methods (dump / setup tlv datastructure to/from file) void setup(generic_file & f); ///< same as the constructor but on an existing object void dump(generic_file & f) const; ///< dumps the tlv contents to file U_16 get_type() const { return type; }; ///< get the TLV type void set_type(U_16 val) { type = val; }; ///< set the TLV type private: U_16 type; void init(generic_file & f); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/compressor_zstd.cpp0000644000175000017500000002530514636066467015314 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STDLIB_H #include #endif } // end extern "C" #include #include "compressor_zstd.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "null_file.hpp" using namespace std; namespace libdar { compressor_zstd::compressor_zstd(generic_file & compressed_side, U_I compression_level) : proto_compressor(compressed_side.get_mode()) { #if LIBZSTD_AVAILABLE compressed = & compressed_side; suspended = false; comp = nullptr; decomp = nullptr; clear_inbuf(); clear_outbuf(); below_tampon = nullptr; no_comp_data = false; U_I min_version = atoi(MIN_MAJ_VERSION_ZSTD)*100*100 + atoi(MIN_MIN_VERSION_ZSTD)*100 + 0; if(ZSTD_versionNumber() < min_version) throw Ecompilation(tools_printf(gettext("need libzstd version greater or equal to %d (current version is %d)"), min_version, ZSTD_versionNumber())); try { switch(get_mode()) { case gf_read_only: decomp = ZSTD_createDStream(); if(decomp == nullptr) throw Ememory("zstd::zstd"); below_tampon_size = ZSTD_DStreamInSize(); above_tampon_size = ZSTD_DStreamOutSize(); flueof = false; break; case gf_write_only: case gf_read_write: // but read operation will fail comp = ZSTD_createCStream(); if(comp == nullptr) throw Ememory("zsts::zstd"); below_tampon_size = ZSTD_CStreamOutSize(); above_tampon_size = ZSTD_CStreamInSize(); flueof = true; break; default: throw SRC_BUG; } setup_context(compression_level); below_tampon = new (nothrow) char[below_tampon_size]; if(below_tampon == nullptr) throw Ememory("zstd::zstd"); } catch(...) { release_mem(); throw; } #else throw Ecompilation(gettext("zstd compression")); #endif } compressor_zstd::~compressor_zstd() { try { terminate(); } catch(...) { // ignore all exceptions } } compression compressor_zstd::get_algo() const { if(suspended) return compression::none; else return compression::zstd; } void compressor_zstd::suspend_compression() { if(!suspended) { compr_flush_write(); reset_compr_engine(); suspended = true; } } void compressor_zstd::resume_compression() { if(suspended) suspended = false; } U_I compressor_zstd::inherited_read(char *a, U_I size) { #if LIBZSTD_AVAILABLE U_I err = 0; U_I wrote = 0; if(suspended) return compressed->read(a, size); switch(get_mode()) { case gf_read_only: break; case gf_read_write: throw Efeature("read-write mode for class compressor_zstd"); case gf_write_only: throw SRC_BUG; default: throw SRC_BUG; } if(decomp == nullptr) throw SRC_BUG; if(below_tampon == nullptr) throw SRC_BUG; if(inbuf.src == nullptr) { inbuf.src = below_tampon; inbuf.size = 0; inbuf.pos = 0; } while(!flueof && wrote < size) { U_I delta_in = below_tampon_size - inbuf.size; if(delta_in > 0 && !no_comp_data) { U_I lu = compressed->read((char *)inbuf.src + inbuf.size, delta_in); if(lu < delta_in) no_comp_data = true; inbuf.size += lu; } outbuf.dst = a + wrote; outbuf.size = size - wrote < above_tampon_size? size - wrote : above_tampon_size; outbuf.pos = 0; err = ZSTD_decompressStream(decomp, &outbuf, &inbuf); if(ZSTD_isError(err)) throw Erange("zstd::read", tools_printf(gettext("Error returned by libzstd while uncompressing data: %s"), ZSTD_getErrorName(err))); if(err == 0) flueof = true; if(inbuf.pos > 0) { // some input data has been consumed if(inbuf.pos < inbuf.size) { // only a part of the data has been consumed (void)memmove(below_tampon, below_tampon + inbuf.pos, inbuf.size - inbuf.pos); inbuf.size -= inbuf.pos; inbuf.pos = 0; } else { // all data has been consumed inbuf.pos = 0; inbuf.size = 0; } } wrote += outbuf.pos; if(no_comp_data && outbuf.pos == 0 && wrote < size && !flueof) throw Erange("zstd::read", gettext("uncompleted compressed stream, some compressed data is missing or corruption occured")); } return wrote; #else throw Ecompilation(gettext("zstd compression")); #endif } void compressor_zstd::inherited_write(const char *a, U_I size) { #if LIBZSTD_AVAILABLE U_I err; U_I lu = 0; U_I next_bs = above_tampon_size; if(suspended) compressed->write(a, size); else { if(comp == nullptr) throw SRC_BUG; if(below_tampon == nullptr) throw SRC_BUG; // we need that to be able to flush_write later on flueof = false; outbuf.dst = below_tampon; outbuf.size = below_tampon_size; while(lu < size) { inbuf.src = (const void *)(a + lu); inbuf.size = size-lu < next_bs ? size-lu : next_bs; inbuf.pos = 0; outbuf.pos = 0; err = ZSTD_compressStream(comp, &outbuf, &inbuf); if(ZSTD_isError(err)) throw Erange("zstd::write", tools_printf(gettext("Error met while giving data for compression to libzstd: %s"), ZSTD_getErrorName(err))); next_bs = err; if(outbuf.pos > 0) compressed->write((char *)outbuf.dst, outbuf.pos); // generic::write never does partial writes lu += inbuf.pos; } } #else throw Ecompilation(gettext("zstd compression")); #endif } void compressor_zstd::reset_compr_engine() { clean_read(); clean_write(); } void compressor_zstd::inherited_truncate(const infinint & pos) { if(pos < get_position()) { compr_flush_write(); compr_flush_read(); clean_read(); } compressed->truncate(pos); } void compressor_zstd::inherited_terminate() { if(get_mode() != gf_read_only) { compr_flush_write(); clean_write(); } else { compr_flush_read(); clean_read(); } release_mem(); } void compressor_zstd::compr_flush_write() { #if LIBZSTD_AVAILABLE if(is_terminated()) throw SRC_BUG; U_I err; if(flueof || get_mode() == gf_read_only) return; outbuf.dst = below_tampon; outbuf.size = below_tampon_size; outbuf.pos = 0; err = ZSTD_endStream(comp, &outbuf); if(ZSTD_isError(err)) throw Erange("zstd::compr_flush_write", tools_printf(gettext("Error met while asking libzstd for compression end: %s"), ZSTD_getErrorName(err))); compressed->write((char *)outbuf.dst, outbuf.pos); while(err > 0) { outbuf.pos = 0; err = ZSTD_flushStream(comp, &outbuf); if(ZSTD_isError(err)) throw Erange("zstd::compr_flush_write", tools_printf(gettext("Error met while asking libzstd to flush data once compression end has been asked: %s"), ZSTD_getErrorName(err))); compressed->write((char *)outbuf.dst, outbuf.pos); } flueof = true; #else throw Ecompilation(gettext("zstd compression")); #endif } void compressor_zstd::compr_flush_read() { #if LIBZSTD_AVAILABLE if(is_terminated()) throw SRC_BUG; if(get_mode() != gf_read_only) return; flueof = false; no_comp_data = false; #else throw Ecompilation(gettext("zstd compression")); #endif } void compressor_zstd::clean_read() { #if LIBZSTD_AVAILABLE if(is_terminated()) throw SRC_BUG; if(get_mode() != gf_read_only) return; flueof = false; no_comp_data = false; clear_inbuf(); clear_outbuf(); (void)ZSTD_initDStream(decomp); #else throw Ecompilation(gettext("zstd compression")); #endif } void compressor_zstd::clean_write() { #if LIBZSTD_AVAILABLE if(is_terminated()) throw SRC_BUG; if(get_mode() == gf_read_only) return; if(!flueof) { null_file null(gf_write_only); generic_file *original_compressed = compressed; compressed = &null; try { compr_flush_write(); // flushing to reset the libzstd engine // but sending compressed data to /dev/null } catch(...) { compressed = original_compressed; throw; } } clear_inbuf(); clear_outbuf(); #else throw Ecompilation(gettext("zstd compression")); #endif } void compressor_zstd::clear_inbuf() { #if LIBZSTD_AVAILABLE inbuf.src = nullptr; inbuf.size = 0; inbuf.pos = 0; #else throw Ecompilation(gettext("zstd compression")); #endif } void compressor_zstd::clear_outbuf() { #if LIBZSTD_AVAILABLE outbuf.dst = nullptr; outbuf.size = 0; outbuf.pos = 0; #else throw Ecompilation(gettext("zstd compression")); #endif } void compressor_zstd::release_mem() { #if LIBZSTD_AVAILABLE if(decomp != nullptr) ZSTD_freeDStream(decomp); if(comp != nullptr) ZSTD_freeCStream(comp); if(below_tampon != nullptr) delete [] below_tampon; #endif } void compressor_zstd::setup_context(U_I compression_level) { #if LIBZSTD_AVAILABLE int err; static const U_I maxcomp = ZSTD_maxCLevel(); switch(get_mode()) { case gf_read_only: if(decomp == nullptr) throw SRC_BUG; err = ZSTD_initDStream(decomp); if(ZSTD_isError(err)) throw Erange("zstd::setup_context", tools_printf(gettext("Error while initializing libzstd for decompression: %s"), ZSTD_getErrorName(err))); break; case gf_write_only: case gf_read_write: if(comp == nullptr) throw SRC_BUG; // setting ZSTD_c_compressionLevel parameter if(compression_level > maxcomp) throw Erange("zstd::setup_context", tools_printf(gettext("the requested compression level (%d) is higher than the maximum available for libzstd: %d"), compression_level, maxcomp)); err = ZSTD_initCStream(comp, compression_level); if(ZSTD_isError(err)) throw Erange("zstd::setup_context", tools_printf(gettext("Error while setting libzstd compression level to %d: %s"), compression_level, ZSTD_getErrorName(err))); break; default: throw SRC_BUG; } #else throw Ecompilation(gettext("zstd compression")); #endif } } // end of namespace dar-2.7.15/src/libdar/trontextual.hpp0000644000175000017500000000456514636066467014457 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file trontextual.hpp /// \brief class trontextual is a contextual variant of class tronc /// \ingroup Private #ifndef TRONTEXTUAL_HPP #define TRONTEXTUAL_HPP #include "../my_config.h" #include "tronc.hpp" #include "infinint.hpp" #include "generic_file.hpp" #include "contextual.hpp" namespace libdar { /// \addtogroup Private /// @{ /// trontextual class is a contextual class tronc, that's all. class trontextual : public tronc, public contextual { public: trontextual(generic_file *f, const infinint & offset, const infinint & size, bool own_f = false); trontextual(generic_file *f, const infinint & offset, const infinint & size, gf_mode mode, bool own_f = false); trontextual(const trontextual & ref) = delete; trontextual(trontextual && ref) noexcept = delete; trontextual & operator = (const trontextual & ref) = delete; trontextual & operator = (trontextual && ref) = delete; ~trontextual() = default; virtual bool is_an_old_start_end_archive() const override { if(ref == nullptr) throw SRC_BUG; return ref->is_an_old_start_end_archive(); }; virtual const label & get_data_name() const override { if(ref == nullptr) throw SRC_BUG; return ref->get_data_name(); }; private: contextual *ref; ///< this is just a pointer to data owned by the inherited class tronc part of this object void init(generic_file *f); }; /// @} } #endif dar-2.7.15/src/libdar/filesystem_tools.hpp0000644000175000017500000001000614636066467015455 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file filesystem_tools.hpp /// \brief a set of tools used by filesystem_* classes /// \ingroup Private #ifndef FILESYSTEM_TOOLS_HPP #define FILESYSTEM_TOOLS_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "infinint.hpp" #include "generic_file.hpp" #include "fsa_family.hpp" #include "cat_all_entrees.hpp" #include "crc.hpp" #include "user_interaction.hpp" #include "path.hpp" #include "fichier_local.hpp" #include namespace libdar { /// \addtogroup Private /// @{ bool filesystem_tools_has_immutable(const cat_inode & arg); void filesystem_tools_set_immutable(const std::string & target, bool val, user_interaction &ui); void filesystem_tools_supprime(user_interaction & ui, const std::string & ref); void filesystem_tools_widen_perm(user_interaction & dialog, const cat_inode & ref, const std::string & chem, comparison_fields what_to_check); void filesystem_tools_make_owner_perm(user_interaction & dialog, const cat_inode & ref, const std::string & chem, comparison_fields what_to_check, const fsa_scope & scope); void filesystem_tools_make_date(const cat_inode & ref, const std::string & chem, comparison_fields what_to_check, const fsa_scope & scope); void filesystem_tools_attach_ea(const std::string &chemin, cat_inode *ino, const mask & ea_mask); bool filesystem_tools_is_nodump_flag_set(user_interaction & dialog, const path & chem, const std::string & filename, bool info); path *filesystem_tools_get_root_with_symlink(user_interaction & dialog, const path & root, bool info_details); mode_t filesystem_tools_get_file_permission(const std::string & path); void filesystem_tools_make_delta_patch(const std::shared_ptr & dialog, const cat_file & existing, const std::string & existing_pathname, const cat_file & patcher, const path & directory); /// create in dirname a brand-new filename which name derives from filename /// \return a read-write object the caller has the duty to destroy, exception thrown /// if no filename could be created fichier_local *filesystem_tools_create_non_existing_file_based_on(const std::shared_ptr & dialog, std::string filename, path where, std::string & new_filename); void filesystem_tools_copy_content_from_to(const std::shared_ptr & dialog, const std::string & source_path, const std::string & destination_path, const crc *expected_crc); /// read the birthtime of target inode /// \param[in] target the path the the inode to read the birthtime of /// \param[out] val the birthtime (only valid if the call returned true) /// \return true if the birthtime could be read with linux specific systemcall bool filesystem_tools_read_linux_birthtime(const std::string & target, datetime & val); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/cat_ignored_dir.hpp0000644000175000017500000000520414636066467015171 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file cat_ignored_dir.hpp /// \brief class used to remember in a catalogue that a cat_directory has been ignored /// \ingroup Private #ifndef CAT_IGNORED_DIR_HPP #define CAT_IGNORED_DIR_HPP #include "../my_config.h" extern "C" { } // end extern "C" #include "cat_inode.hpp" #include "cat_directory.hpp" namespace libdar { /// \addtogroup Private /// @{ /// the ignored cat_directory class, to be promoted later as empty cat_directory if needed class cat_ignored_dir : public cat_inode { public: cat_ignored_dir(const cat_directory &target) : cat_inode(target) {}; cat_ignored_dir(const std::shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, bool small) : cat_inode(dialog, pdesc, reading_ver, saved_status::not_saved, small) { throw SRC_BUG; }; cat_ignored_dir(const cat_ignored_dir & ref) = default; cat_ignored_dir(cat_ignored_dir && ref) noexcept = default; cat_ignored_dir & operator = (const cat_ignored_dir & ref) = default; cat_ignored_dir & operator = (cat_ignored_dir && ref) = default; ~cat_ignored_dir() = default; bool operator == (const cat_entree & ref) const override; virtual unsigned char signature() const override { return 'j'; }; virtual std::string get_description() const override { return "ignored directory"; }; cat_entree *clone() const override { return new (std::nothrow) cat_ignored_dir(*this); }; protected: virtual void inherited_dump(const pile_descriptor & pdesc, bool small) const override; // behaves like an empty cat_directory }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/tuyau.cpp0000644000175000017500000002627614636066467013233 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" #include "tuyau.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "integers.hpp" #include "cygwin_adapt.hpp" #define BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFFER_SIZE #undef BUFFER_SIZE #define BUFFER_SIZE SSIZE_MAX #endif #endif using namespace std; namespace libdar { static gf_mode generic_file_get_mode(S_I fd); tuyau::tuyau(const shared_ptr & dialog, S_I fd) : generic_file(generic_file_get_mode(fd)), mem_ui(dialog) { gf_mode tmp; if(fd < 0) throw Erange("tuyau::tuyau", "Bad file descriptor given"); tmp = generic_file_get_mode(fd); if(tmp == gf_read_write) throw Erange("tuyau::tuyau", tools_printf("A pipe cannot be in read and write mode at the same time, I need precision on the mode to use for the given filedscriptor")); pipe_mode = pipe_fd; filedesc = fd; position = 0; other_end_fd = -1; has_one_to_read = false; } tuyau::tuyau(const shared_ptr & dialog, S_I fd, gf_mode mode) : generic_file(mode), mem_ui(dialog) { gf_mode tmp; if(fd < 0) throw Erange("tuyau::tuyau", "Bad file descriptor given"); if(mode == gf_read_write) throw Erange("tuyau::tuyau", tools_printf("A pipe cannot be in read and write mode at the same time")); tmp = generic_file_get_mode(fd); if(tmp != gf_read_write && tmp != mode) throw Erange("tuyau::tuyau", tools_printf("%s cannot be restricted to %s", generic_file_get_name(tmp), generic_file_get_name(mode))); pipe_mode = pipe_fd; filedesc = fd; position = 0; other_end_fd = -1; has_one_to_read = false; } tuyau::tuyau(const shared_ptr & dialog, const string & filename, gf_mode mode) : generic_file(mode), mem_ui(dialog) { pipe_mode = pipe_path; chemin = filename; position = 0; other_end_fd = -1; has_one_to_read = false; } tuyau::tuyau(const shared_ptr & dialog) : generic_file(gf_write_only), mem_ui(dialog) { int tube[2]; if(pipe(tube) < 0) throw Erange("tuyau::tuyau", string(gettext("Error while creating anonymous pipe: ")) + tools_strerror_r(errno)); pipe_mode = pipe_both; position = 0; filedesc = tube[1]; other_end_fd = tube[0]; has_one_to_read = false; } tuyau::~tuyau() { try { terminate(); } catch(...) { // ignore all exceptions } } int tuyau::get_read_fd() const { if(is_terminated()) throw SRC_BUG; if(pipe_mode == pipe_both) return other_end_fd; else throw Erange("tuyau::get_read_fd", gettext("Pipe's other end is not known, cannot provide a filedescriptor on it")); } void tuyau::close_read_fd() { if(is_terminated()) throw SRC_BUG; if(pipe_mode == pipe_both) { close(other_end_fd); pipe_mode = pipe_fd; } else throw Erange("tuyau::get_read_fd", gettext("Pipe's other end is not known, cannot close any filedescriptor pointing on it")); } void tuyau::do_not_close_read_fd() { if(is_terminated()) throw SRC_BUG; if(pipe_mode == pipe_both) pipe_mode = pipe_fd; else throw Erange("tuyau::get_read_fd", "Pipe's other end is not known, there is no reason to ask not to close a filedescriptor on it"); } bool tuyau::skippable(skippability direction, const infinint & amount) { if(get_mode() == gf_read_only) return direction == skip_forward; else return false; } bool tuyau::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; if(pos < position) throw Erange("tuyau::skip", "Skipping backward is not possible on a pipe"); else if(pos == position) return true; else return read_and_drop(pos - position); } bool tuyau::skip_to_eof() { if(is_terminated()) throw SRC_BUG; if(get_mode() != gf_write_only) return read_to_eof(); else return true; } bool tuyau::skip_relative(S_I x) { if(is_terminated()) throw SRC_BUG; if(x < 0) throw Erange("tuyau::skip", "Skipping backward is not possible on a pipe"); else return read_and_drop(x); } bool tuyau::has_next_to_read() { if(is_terminated()) throw SRC_BUG; if(has_one_to_read) return true; else { S_I ret = ::read(filedesc, &next_to_read, 1); if(ret <= 0) return false; else { has_one_to_read = true; return true; } } } U_I tuyau::inherited_read(char *a, U_I size) { S_I ret; U_I lu = 0; #ifdef MUTEX_WORKS check_self_cancellation(); #endif ouverture(); switch(pipe_mode) { case pipe_fd: case pipe_both: break; case pipe_path: throw SRC_BUG; default: throw SRC_BUG; } if(size == 0) return 0; if(has_one_to_read) { a[lu] = next_to_read; ++lu; has_one_to_read = false; } do { #ifdef SSIZE_MAX U_I to_read = size - lu > SSIZE_MAX ? SSIZE_MAX : size - lu; #else U_I to_read = size - lu; #endif ret = ::read(filedesc, a+lu, to_read); if(ret < 0) { switch(errno) { case EINTR: break; case EIO: throw Ehardware("tuyau::inherited_read", ""); default: throw Erange("tuyau::inherited_read", string(gettext("Error while reading from pipe: "))+tools_strerror_r(errno)); } } else if(ret > 0) lu += ret; } while(lu < size && ret > 0); position += lu; return lu; } void tuyau::inherited_write(const char *a, U_I size) { U_I total = 0; ssize_t ret; #ifdef SSIZE_MAX static const U_I step = SSIZE_MAX/2; #else const U_I step = size; // which is no limit... #endif #ifdef MUTEX_WORKS check_self_cancellation(); #endif ouverture(); switch(pipe_mode) { case pipe_fd: case pipe_both: break; case pipe_path: throw SRC_BUG; default: throw SRC_BUG; } while(total < size) { if(size - total > step) ret = ::write(filedesc, a+total, step); else ret = ::write(filedesc, a+total, size - total); if(ret < 0) { switch(errno) { case EINTR: break; case EIO: throw Ehardware("tuyau::inherited_write", string(gettext("Error while writing data to pipe: ")) + tools_strerror_r(errno)); case ENOSPC: get_ui().pause(gettext("No space left on device, you have the opportunity to make room now. When ready : can we continue ?")); break; default : throw Erange("tuyau::inherited_write", string(gettext("Error while writing data to pipe: ")) + tools_strerror_r(errno)); } } else total += (U_I)ret; } position += total; } void tuyau::inherited_terminate() { switch(pipe_mode) { case pipe_both: close(other_end_fd); // no break here ! case pipe_fd: other_end_fd = -1; close(filedesc); filedesc = -1; break; case pipe_path: break; default: throw SRC_BUG; } } void tuyau::ouverture() { if(pipe_mode == pipe_path) { S_I flag; switch(get_mode()) { case gf_read_only: flag = O_RDONLY; break; case gf_write_only: flag = O_WRONLY; break; case gf_read_write: flag = O_RDWR; break; default: throw SRC_BUG; } filedesc = ::open(chemin.c_str(), flag|O_BINARY); if(filedesc < 0) throw Erange("tuyau::ouverture", string(gettext("Error opening pipe: "))+tools_strerror_r(errno)); pipe_mode = pipe_fd; } } bool tuyau::read_and_drop(infinint byte) { char buffer[BUFFER_SIZE]; U_I u_step; U_I step, max_i_step = 0; S_I lu; bool eof = false; max_i_step = int_tools_maxof_aggregate(max_i_step); if(max_i_step <= 0) throw SRC_BUG; // error in max positive value calculation, just above if(max_i_step > BUFFER_SIZE) max_i_step = BUFFER_SIZE; // max read a time if(get_mode() != gf_read_only) throw Erange("tuyau::read_and_drop", "Cannot skip in pipe in writing mode"); u_step = 0; byte.unstack(u_step); do { while(u_step > 0 && !eof) { if(u_step > max_i_step) step = max_i_step; else step = u_step; lu = read(buffer, step); if(lu < 0) throw SRC_BUG; if((U_I)lu < step) eof = true; u_step -= lu; } if(!eof) { u_step = 0; byte.unstack(u_step); } } while(u_step > 0 && !eof); if(!byte.is_zero()) throw SRC_BUG; return !eof; } bool tuyau::read_to_eof() { char buffer[BUFFER_SIZE]; S_I lu = 0; if(get_mode() != gf_read_only) throw Erange("tuyau::read_and_drop", "Cannot skip in pipe in writing mode"); while((lu = read(buffer, BUFFER_SIZE)) > 0) position += lu; return true; } static gf_mode generic_file_get_mode(S_I fd) { S_I flags = fcntl(fd, F_GETFL) & O_ACCMODE; gf_mode ret; switch(flags) { case O_RDONLY: ret = gf_read_only; break; case O_WRONLY: ret = gf_write_only; break; case O_RDWR: ret = gf_read_write; break; default: throw Erange("generic_file_get_mode", gettext("File mode is neither read nor write")); } return ret; } } // end of namespace dar-2.7.15/src/libdar/cat_device.cpp0000644000175000017500000001054714636066467014144 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { // to allow compilation under Cygwin we need // else Cygwin's lack __int16_t symbol !?! #if HAVE_SYS_TYPES_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #ifdef STDC_HEADERS #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" #include "cat_device.hpp" #include "tools.hpp" using namespace std; namespace libdar { cat_device::cat_device(const infinint & uid, const infinint & gid, U_16 perm, const datetime & last_access, const datetime & last_modif, const datetime & last_change, const string & name, U_16 major, U_16 minor, const infinint & fs_dev) : cat_inode(uid, gid, perm, last_access, last_modif, last_change, name, fs_dev) { xmajor = major; xminor = minor; set_saved_status(saved_status::saved); } cat_device::cat_device(const shared_ptr & dialog, const smart_pointer & pdesc, const archive_version & reading_ver, saved_status saved, bool small) : cat_inode(dialog, pdesc, reading_ver, saved, small) { U_16 tmp; generic_file *ptr = nullptr; pdesc->check(small); if(small) ptr = pdesc->esc; else ptr = pdesc->stack; if(saved == saved_status::saved) { if(ptr->read((char *)&tmp, sizeof(tmp)) != sizeof(tmp)) throw Erange("special::special", gettext("missing data to build a special device")); xmajor = ntohs(tmp); if(ptr->read((char *)&tmp, sizeof(tmp)) != sizeof(tmp)) throw Erange("special::special", gettext("missing data to build a special device")); xminor = ntohs(tmp); } } bool cat_device::operator == (const cat_entree & ref) const { const cat_device *ref_dev = dynamic_cast(&ref); if(ref_dev == nullptr) return false; else return xmajor == ref_dev->xmajor && xminor == ref_dev->xminor && cat_inode::operator == (ref); } void cat_device::inherited_dump(const pile_descriptor & pdesc, bool small) const { U_16 tmp; generic_file *ptr = nullptr; pdesc.check(small); if(small) ptr = pdesc.esc; else ptr = pdesc.stack; cat_inode::inherited_dump(pdesc, small); if(get_saved_status() == saved_status::saved) { tmp = htons(xmajor); ptr->write((char *)&tmp, sizeof(tmp)); tmp = htons(xminor); ptr->write((char *)&tmp, sizeof(tmp)); } } void cat_device::sub_compare(const cat_inode & other, bool isolated_mode) const { const cat_device *d_other = dynamic_cast(&other); if(d_other == nullptr) throw SRC_BUG; // bug in cat_inode::compare if(get_saved_status() == saved_status::saved && d_other->get_saved_status() == saved_status::saved) { if(get_major() != d_other->get_major()) throw Erange("cat_device::sub_compare", tools_printf(gettext("devices have not the same major number: %d <--> %d"), get_major(), d_other->get_major())); if(get_minor() != d_other->get_minor()) throw Erange("cat_device::sub_compare", tools_printf(gettext("devices have not the same minor number: %d <--> %d"), get_minor(), d_other->get_minor())); } } } // end of namespace dar-2.7.15/src/libdar/lz4_module.hpp0000644000175000017500000000545614636066467014144 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file lz4_module.hpp /// \brief per block encryption using LZ4 algorithm/library /// \ingroup Private /// /// we use libfar cryptography infrastructure /// (tronconneuse/parallel_tronconneuse/crypto_module) because /// it makes sense for a per block compression (suitable for /// multithreading) by opposition to streamed compression per /// saved file. Each file will be compressed by segment, /// resulting in non optimum compression ratio but speed gain. /// the larger the block size is, the better closer the /// compression ratio will be to the optimum #ifndef LZ4_MODULE_HPP #define LZ4_MODULE_HPP extern "C" { #if HAVE_LZ4_H #include #endif } #include "../my_config.h" #include "compress_module.hpp" namespace libdar { /// \addtogroup Private /// @{ class lz4_module: public compress_module { public: lz4_module(U_I compression_level = 9); lz4_module(const lz4_module & ref); lz4_module(lz4_module && ref) noexcept; lz4_module & operator = (const lz4_module & ref); lz4_module & operator = (lz4_module && ref) noexcept; virtual ~lz4_module() noexcept; // inherited from compress_module interface virtual compression get_algo() const override { return compression::lz4; }; virtual U_I get_max_compressing_size() const override; virtual U_I get_min_size_to_compress(U_I clear_size) const override; virtual U_I compress_data(const char *normal, const U_I normal_size, char *zip_buf, U_I zip_buf_size) const override; virtual U_I uncompress_data(const char *zip_buf, const U_I zip_buf_size, char *normal, U_I normal_size) const override; virtual std::unique_ptr clone() const override; private: U_I acceleration; char *state; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/memory_file.cpp0000644000175000017500000000510314636066467014355 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "memory_file.hpp" using namespace std; namespace libdar { bool memory_file::skip(const infinint & pos) { if(is_terminated()) throw SRC_BUG; if(pos >= data.size()) { position = data.size(); return false; } else { position = pos; return true; } } bool memory_file::skip_to_eof() { if(is_terminated()) throw SRC_BUG; position = data.size(); return true; } bool memory_file::skip_relative(S_I x) { bool ret = false; if(is_terminated()) throw SRC_BUG; if(x < 0) { U_I tx = -x; if(position < tx) { position = 0; ret = false; } else { position -= tx; ret = true; } } else { position += x; if(position > data.size()) { position = data.size(); ret = false; } else ret = true; } return ret; } U_I memory_file::inherited_read(char *a, U_I size) { U_I ret = 0; while(ret < size && position < data.size()) { *(a++) = (char)(data[position]); ++ret; ++position; } return ret; } void memory_file::inherited_write(const char *a, U_I size) { U_I ret = 0; if(size == 0) return; while(ret < size && position < data.size()) { data[position] = (unsigned char)(*(a++)); ret++; ++position; } data.insert_bytes_at_iterator(data.end(), (unsigned char *)(a), size - ret); position += size - ret; } void memory_file::inherited_truncate(const infinint & pos) { data.truncate(pos); if(position > pos) position = pos; } } // end of namespace dar-2.7.15/src/libdar/header_version.cpp0000644000175000017500000004571214636066467015055 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif } // end of extern "C" #include "header_version.hpp" #include "integers.hpp" #include "tools.hpp" #include "deci.hpp" #include "header_flags.hpp" #define LIBDAR_URL_VERSION "http://dar.linux.free.fr/doc/Notes.html#Dar_version_naming" using namespace std; namespace libdar { // FLAG VALUES // flags of the historical bytes (oldest), read/wrote last (or alone if no extra bits are set) static constexpr U_I FLAG_SAVED_EA_ROOT = 0x80; ///< no more used since version "05" static constexpr U_I FLAG_SAVED_EA_USER = 0x40; ///< no more used since version "05" static constexpr U_I FLAG_SCRAMBLED = 0x20; ///< scrambled or strong encryption used static constexpr U_I FLAG_SEQUENCE_MARK = 0x10; ///< escape sequence marks present for sequential reading static constexpr U_I FLAG_INITIAL_OFFSET = 0x08; ///< whether the header contains the initial offset (size of clear data before encrypted) static constexpr U_I FLAG_HAS_CRYPTED_KEY = 0x04; ///< the header contains a symmetrical key encrypted with asymmetrical algorithm static constexpr U_I FLAG_HAS_REF_SLICING = 0x02; ///< the header contains the slicing information of the archive of reference (used for isolated catalogue) static constexpr U_I FLAG_IS_RESERVED_1 = 0x01; ///< this flag is reserved meaning two bytes length bitfield static constexpr U_I FLAG_IS_RESERVED_2 = 0x0100; ///< reserved for future use static constexpr U_I FLAG_ARCHIVE_IS_SIGNED = 0x0200; ///< archive is signed static constexpr U_I FLAG_HAS_KDF_PARAM = 0x0400; ///< archive header contains salt and non default interaction count static constexpr U_I FLAG_HAS_COMPRESS_BS = 0x0800; ///< archive header contains a compression block size (else it is assumed equal to zero) // when adding a new flag, all_flags_known() must be updated to pass sanity checks static const U_I HEADER_CRC_SIZE = 2; //< CRC width (deprecated, now only used when reading old archives) // check we are not facing a more recent format than what we know, this would fail CRC calculation before reporting the real reason of the issue static bool all_flags_known(header_flags flag); // header_version::header_version() { algo_zip = compression::none; cmd_line = ""; initial_offset = 0; sym = crypto_algo::none; crypted_key = nullptr; ref_layout = nullptr; ciphered = false; arch_signed = false; iteration_count = PRE_FORMAT_10_ITERATION; kdf_hash = hash_algo::sha1; // used by default salt = ""; compr_bs = 0; has_kdf_params = false; } void header_version::read(generic_file & f, user_interaction & dialog, bool lax_mode) { crc *ctrl = nullptr; char tmp; header_flags flag; f.reset_crc(HEADER_CRC_SIZE); try { edition.read(f); } catch(Egeneric & e) { if(lax_mode) { string answ, answ2; U_I equivalent, equivalent2; bool ok = false; dialog.message(gettext("LAX MODE: Failed to read the archive format version.")); do { dialog.message(gettext("LAX MODE: archive format is composed of two parts, the major number and the minor number, separated by a dot. For example with a format of \"11.1\", the major is 11 and a the minor is 1. If no dot is specified like in \"6\" this means a major of 6 with a minor is zero.")); dialog.message(tools_printf(gettext("LAX MODE: To determine the archive format, use the table at %s to find it out depending on the release version, (for example if this archive has been created using dar release 2.3.4 to 2.3.7 the format is 6 (major = 6, minor = 0)"), LIBDAR_URL_VERSION)); answ = dialog.get_string(tools_printf(gettext("LAX MODE: Please provide the format MAJOR number: ")), true); answ2 = dialog.get_string(tools_printf(gettext("LAX MODE: Please provide the format MINOR number: ")), true); if(tools_my_atoi(answ.c_str(), equivalent) && tools_my_atoi(answ2.c_str(), equivalent2)) edition = equivalent; else { dialog.pause(tools_printf(gettext("LAX MODE: \"%S.%S\" is not a valid format"), &answ, &answ2)); continue; } try { dialog.pause(tools_printf(gettext("LAX MODE: Using archive format \"%d.%d\"?"), equivalent, equivalent2)); ok = true; } catch(Euser_abort & e) { ok = false; } } while(!ok); } else throw; } if(f.read(&tmp, 1) == 1) // compression algo { bool ok = false; do { try { algo_zip = char2compression(tmp); ok = true; } catch(Erange & e) { bool loop = false; if(!lax_mode) throw; do { string answ = dialog.get_string(gettext("LAX MODE: Unknown compression algorithm used, assuming data corruption occurred. Please help me, answering with one of the following words \"none\", \"gzip\", \"bzip2\", \"lzo\", \"xz\", \"zstd\" or \"lz4\" at the next prompt:"), true); if(answ == gettext("none")) { tmp = compression2char(compression::none); loop = false; } else if(answ == gettext("gzip")) { tmp = compression2char(compression::gzip); loop = false; } else if(answ == gettext("bzip2")) { tmp = compression2char(compression::bzip2); loop = false; } else if(answ == gettext("lzo")) { tmp = compression2char(compression::lzo); loop = false; } else if(answ == gettext("xz")) { tmp = compression2char(compression::xz); loop = false; } else if(answ == gettext("zstd")) { tmp = compression2char(compression::zstd); loop = false; } else if(answ == gettext("lz4")) { tmp = compression2char(compression::lz4); loop = false; } else loop = true; } while(loop); } } while(!ok); } else throw Erange("header_version::read", gettext("Reached End of File while reading archive header_version data structure")); tools_read_string(f, cmd_line); if(edition > 1) flag.read(f); else flag.clear(); // flag has been at edition 2 if(flag.is_set(FLAG_INITIAL_OFFSET)) { initial_offset.read(f); } else initial_offset = 0; if(flag.is_set(FLAG_SCRAMBLED)) { ciphered = true; if(edition >= 9) { if(f.read(&tmp, sizeof(tmp)) != 1) throw Erange("header_version::read", gettext("Reached End of File while reading archive header_version data structure")); try { sym = char_2_crypto_algo(tmp); } catch(Erange & e) { if(!lax_mode) throw; dialog.printf("Unknown crypto algorithm used in archive, ignoring that field and simply assuming the archive has been encrypted, if not done you will need to specify the crypto algorithm to use in order to read this archive"); sym = crypto_algo::none; } } else // unknown ciphering algorithm used (old archive format) or no encryption // not coherent with flag which has the FLAG_SCRAMBLED bit set // but that this way we record that the crypto algo has // to be provided by the user sym = crypto_algo::none; } else { ciphered = false; sym = crypto_algo::none; // no crypto used, coherent with flag } has_tape_marks = flag.is_set(FLAG_SEQUENCE_MARK); if(edition < 8 && has_tape_marks) { if(lax_mode) has_tape_marks = false; // Escape sequence marks appeared at revision 08 else throw Erange("header_version::read", gettext("Corruption met while reading header_version data structure")); } if(crypted_key != nullptr) { delete crypted_key; crypted_key = nullptr; } if(flag.is_set(FLAG_HAS_CRYPTED_KEY)) { infinint key_size = f; // reading key_size from the header_version in archive crypted_key = new (nothrow) memory_file(); if(crypted_key == nullptr) throw Ememory("header_version::read"); if(f.copy_to(*crypted_key, key_size) != key_size) throw Erange("header_version::read", gettext("Missing data for encrypted symmetrical key")); } if(flag.is_set(FLAG_HAS_REF_SLICING)) { try { if(ref_layout == nullptr) ref_layout = new (nothrow) slice_layout(); if(ref_layout == nullptr) throw Ememory("header_version::read"); ref_layout->read(f); } catch(Egeneric & e) { if(lax_mode) { dialog.message(gettext("Error met while reading archive of reference slicing layout, ignoring this field and continuing")); clear_slice_layout(); } else throw; } } else clear_slice_layout(); arch_signed = flag.is_set(FLAG_ARCHIVE_IS_SIGNED); if(flag.is_set(FLAG_HAS_KDF_PARAM)) { unsigned char tmp_hash; infinint salt_size(f); // reading salt_size from file has_kdf_params = true; tools_read_string_size(f, salt, salt_size); iteration_count.read(f); f.read((char *)&tmp_hash, 1); try { kdf_hash = char_to_hash_algo(tmp_hash); if(kdf_hash == hash_algo::none) throw Erange("header_version::read", gettext("valid hash algoritm needed for key derivation function")); } catch(Erange & e) { if(lax_mode) { string msg = e.get_message(); // msg has two roles: 1)display error message to user 2) get answer from user bool ok = false; do { dialog.message(msg); msg = dialog.get_string(gettext("please indicate the hash algoritm to use for key derivation function '1' for sha1, '5' for sha512, 'm' for md5, or 'q' to abort: "), true); try { if(msg.size() == 1) { tmp_hash = msg.c_str()[0]; if(tmp_hash == 'q') { kdf_hash = hash_algo::none; ok = true; } else { kdf_hash = char_to_hash_algo(tmp_hash); ok = true; } } else dialog.message(gettext("please answer with a single character")); } catch(Erange & e) { msg = e.get_message(); } } while(!ok); if(kdf_hash == hash_algo::none) throw; } else throw; } } else { salt = ""; iteration_count = PRE_FORMAT_10_ITERATION; kdf_hash = hash_algo::sha1; } if(flag.is_set(FLAG_HAS_COMPRESS_BS)) compr_bs.read(f); else compr_bs = 0; ctrl = f.get_crc(); if(ctrl == nullptr) throw SRC_BUG; try { if(edition == empty_archive_version()) { if(lax_mode) dialog.message(gettext("Consistency check failed for archive header")); else throw Erange("header_version::read", gettext("Consistency check failed for archive header")); } if(edition > 7) { try { crc *coh = create_crc_from_file(f); if(coh == nullptr) throw SRC_BUG; try { if(typeid(*coh) != typeid(*ctrl)) { if(coh->get_size() != ctrl->get_size()) throw SRC_BUG; else throw SRC_BUG; // both case lead to a bug, but we need to know which one is met } if(*coh != *ctrl) { if(lax_mode) dialog.message(gettext("Consistency check failed for archive header")); else throw Erange("header_version::read", gettext("Consistency check failed for archive header")); } } catch(...) { if(coh != nullptr) delete coh; throw; } if(coh != nullptr) delete coh; } catch(...) { if(!all_flags_known(flag)) dialog.message(gettext("Unknown flag found in archive header/trailer, ignoring the CRC mismatch possibly due to the unknown corresponding field")); else throw; } } if(initial_offset.is_zero()) initial_offset = f.get_position(); } catch(...) { if(ctrl != nullptr) delete ctrl; throw; } if(ctrl != nullptr) delete ctrl; } void header_version::write(generic_file &f) const { crc *ctrl = nullptr; char tmp; header_flags flag; // preparing the data if(!initial_offset.is_zero()) flag.set_bits(FLAG_INITIAL_OFFSET); // adding it to the flag if(crypted_key != nullptr) flag.set_bits(FLAG_HAS_CRYPTED_KEY); if(ref_layout != nullptr) flag.set_bits(FLAG_HAS_REF_SLICING); if(has_tape_marks) flag.set_bits(FLAG_SEQUENCE_MARK); if(sym != crypto_algo::none) flag.set_bits(FLAG_SCRAMBLED); // Note: we cannot set this flag (even if ciphered is true) if we do not know the crypto algo // as since version 9 the presence of this flag implies the existence // of the crypto algorithm in the header/trailer and we will always // write down a header/version of the latest known format (thus greater or // equal to 9). if(arch_signed) flag.set_bits(FLAG_ARCHIVE_IS_SIGNED); if(salt.size() > 0) flag.set_bits(FLAG_HAS_KDF_PARAM); if(compr_bs > 0) flag.set_bits(FLAG_HAS_COMPRESS_BS); if(!all_flags_known(flag)) throw SRC_BUG; // all_flag_known has not been updated with new flags // writing down the data f.reset_crc(HEADER_CRC_SIZE); edition.dump(f); tmp = compression2char(algo_zip); f.write(&tmp, sizeof(tmp)); tools_write_string(f, cmd_line); flag.dump(f); if(initial_offset != 0) initial_offset.dump(f); if(sym != crypto_algo::none) { tmp = crypto_algo_2_char(sym); f.write(&tmp, sizeof(tmp)); } if(crypted_key != nullptr) { crypted_key->size().dump(f); crypted_key->skip(0); crypted_key->copy_to(f); } if(ref_layout != nullptr) ref_layout->write(f); if(salt.size() > 0) { unsigned char tmp_hash = hash_algo_to_char(kdf_hash); infinint salt_size = salt.size(); salt_size.dump(f); tools_write_string_all(f, salt); iteration_count.dump(f); f.write((char *)&tmp_hash, 1); } if(compr_bs > 0) compr_bs.dump(f); ctrl = f.get_crc(); if(ctrl == nullptr) throw SRC_BUG; try { ctrl->dump(f); } catch(...) { if(ctrl != nullptr) delete ctrl; throw; } if(ctrl != nullptr) delete ctrl; } void header_version::set_kdf_hash(hash_algo algo) { if(algo == hash_algo::none) throw Erange("header_version::set_kdf_hash", gettext("invalid hash algorithm provided for key derivation function")); kdf_hash = algo; has_kdf_params = true; } string header_version::get_sym_crypto_name() const { if(get_edition() >= 9) return crypto_algo_2_string(get_sym_crypto_algo()); else return is_ciphered() ? gettext("yes") : gettext("no"); } string header_version::get_asym_crypto_name() const { if((get_edition() >= 9) && (get_crypted_key() != nullptr)) return "gnupg"; else return gettext("none"); } void header_version::display(user_interaction & dialog) const { string algo = compression2string(get_compression_algo()); string sym_str = get_sym_crypto_name(); string asym = get_asym_crypto_name(); string xsigned = is_signed() ? gettext("yes") : gettext("no"); string kdf_iter = deci(iteration_count).human(); string hashing = hash_algo_to_string(kdf_hash); dialog.printf(gettext("Archive version format : %s"), get_edition().display().c_str()); dialog.printf(gettext("Compression algorithm used : %S"), &algo); dialog.printf(gettext("Compression block size used : %i"), &compr_bs); dialog.printf(gettext("Symmetric key encryption used : %S"), &sym_str); dialog.printf(gettext("Asymmetric key encryption used : %S"), &asym); dialog.printf(gettext("Archive is signed : %S"), &xsigned); dialog.printf(gettext("Sequential reading marks : %s"), (get_tape_marks() ? gettext("present") : gettext("absent"))); dialog.printf(gettext("User comment : %S"), &(get_command_line())); if(has_kdf_params) { dialog.printf(gettext("KDF iteration count : %S"), &kdf_iter); dialog.printf(gettext("KDF hash algorithm : %S"), &hashing); dialog.printf(gettext("Salt size : %d byte%c"), salt.size(), salt.size() > 1 ? 's' : ' '); } } void header_version::clear() { edition = archive_version(); algo_zip = compression::none; cmd_line = ""; initial_offset = 0; sym = crypto_algo::none; clear_crypted_key(); clear_slice_layout(); has_tape_marks = false; ciphered = false; arch_signed = false; iteration_count = PRE_FORMAT_10_ITERATION; kdf_hash = hash_algo::sha1; compr_bs = 0; } void header_version::copy_from(const header_version & ref) { edition = ref.edition; algo_zip = ref.algo_zip; cmd_line = ref.cmd_line; initial_offset = ref.initial_offset; sym = ref.sym; if(ref.crypted_key != nullptr) { crypted_key = new (nothrow) memory_file(*ref.crypted_key); if(crypted_key == nullptr) throw Ememory("header_version::copy_from"); } else crypted_key = nullptr; if(ref.ref_layout != nullptr) { ref_layout = new (nothrow) slice_layout(*ref.ref_layout); if(ref_layout == nullptr) throw Ememory("header_version::copy_from"); } else ref_layout = nullptr; has_tape_marks = ref.has_tape_marks; ciphered = ref.ciphered; arch_signed = ref.arch_signed; has_kdf_params = ref.has_kdf_params; salt = ref.salt; iteration_count = ref.iteration_count; kdf_hash = ref.kdf_hash; compr_bs = ref.compr_bs; } void header_version::move_from(header_version && ref) noexcept { edition = move(ref.edition); algo_zip = move(ref.algo_zip); cmd_line = move(ref.cmd_line); initial_offset = move(ref.initial_offset); sym = move(ref.sym); swap(crypted_key, ref.crypted_key); swap(ref_layout, ref.ref_layout); has_tape_marks = move(ref.has_tape_marks); ciphered = move(ref.ciphered); arch_signed = move(ref.arch_signed); has_kdf_params = move(ref.has_kdf_params); salt = move(ref.salt); iteration_count = move(ref.iteration_count); kdf_hash = move(ref.kdf_hash); compr_bs = move(ref.compr_bs); } void header_version::detruit() { clear_crypted_key(); clear_slice_layout(); } static bool all_flags_known(header_flags flag) { U_I bf = 0; bf |= FLAG_SAVED_EA_ROOT; bf |= FLAG_SAVED_EA_USER; bf |= FLAG_SCRAMBLED; bf |= FLAG_SEQUENCE_MARK; bf |= FLAG_INITIAL_OFFSET; bf |= FLAG_HAS_CRYPTED_KEY; bf |= FLAG_HAS_REF_SLICING; bf |= FLAG_ARCHIVE_IS_SIGNED; bf |= FLAG_HAS_KDF_PARAM; bf |= FLAG_HAS_COMPRESS_BS; flag.unset_bits(bf); return flag.is_all_cleared(); } } // end of namespace dar-2.7.15/src/libdar/ea_filesystem.hpp0000644000175000017500000000716714636066467014720 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file ea_filesystem.hpp /// \brief filesystem dependent Extended Attributes operations /// \ingroup Private /// /// This file contains a set of routines used to manipulate /// (read, write or test the presence of) Extended Attributes #ifndef EA_FILESYSTEM_HPP #define EA_FILESYSTEM_HPP #include "../my_config.h" #include #include "ea.hpp" #include "mask.hpp" namespace libdar { /// \addtogroup Private /// @{ /// read EA associated to a given file /// \param[in] chemin is the path to the file to read attributes of /// \param[in] filter is a mask that defines which attributes names have to be considered only /// \return nullptr if no EA have been found under the specified mask, or returns a newly allocated object /// that the caller has the responsibility to delete when no more needed extern ea_attributs * ea_filesystem_read_ea(const std::string & chemin, const mask & filter); /// overwrite some attribute to a given file's attribute list /// \param[in] chemin is the path of the file to write attribute to /// \param[in] val is a list of attribute amoung which a subset will be added to file's attribute list /// \param[in] filter a mask that define which attribute's names will be written and which will be ignored from the given list /// \return true if some attribute could be set /// \note if an EA is already present, it is not modified unless the mask covers its name and a value /// is available for that attribute name in the given list. extern bool ea_filesystem_write_ea(const std::string & chemin, const ea_attributs & val, const mask & filter); /// remove all EA of a given file that match a given mask /// \param[in] name is the filename which EA must be altered /// \param[in] filter is a mask that defines which EA names have to be removed /// \note unless the given mask is logically equivalent to bool_mask(true) some /// EA may remain associated to the file after the operation. extern void ea_filesystem_clear_ea(const std::string & name, const mask & filter); /// test the presence of EA for a given file /// \param[in] name is the filename which EA presence must be check against /// \return true if at least one EA has been found extern bool ea_filesystem_has_ea(const std::string & name); /// test the presence of EA for a given file /// \param[in] name is the filename which EA presence must be check against /// \param[in] filter is a mask that defines which attributes names to only consider /// \return true if at least one EA has been found extern bool ea_filesystem_has_ea(const std::string & name, const mask & filter); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/libdar_xform.cpp0000644000175000017500000000747114636066467014530 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include #include #include "libdar_xform.hpp" #include "sar.hpp" #include "trivial_sar.hpp" #include "macro_tools.hpp" #include "i_libdar_xform.hpp" #include "nls_swap.hpp" using namespace std; namespace libdar { libdar_xform::libdar_xform(const shared_ptr & ui, const string & chem, const string & basename, const string & extension, const infinint & min_digits, const string & execute) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_libdar_xform(ui, chem, basename, extension, min_digits, execute)); if(!pimpl) throw Ememory("libdar_xform::libdar_xform"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } libdar_xform::libdar_xform(const shared_ptr & ui, const std::string & pipename) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_libdar_xform(ui, pipename)); if(!pimpl) throw Ememory("libdar_xform::libdar_xform"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } libdar_xform::libdar_xform(libdar_xform && ref) noexcept = default; libdar_xform & libdar_xform::operator = (libdar_xform && ref) noexcept = default; libdar_xform::~libdar_xform() = default; libdar_xform::libdar_xform(const shared_ptr & ui, int filedescriptor) { NLS_SWAP_IN; try { pimpl.reset(new (nothrow) i_libdar_xform(ui, filedescriptor)); if(!pimpl) throw Ememory("libdar_xform::libdar_xform"); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void libdar_xform::xform_to(const string & chem, const string & basename, const string & extension, bool allow_over, bool warn_over, const infinint & pause, const infinint & first_slice_size, const infinint & slice_size, const string & slice_perm, const string & slice_user, const string & slice_group, hash_algo hash, const infinint & min_digits, const string & execute) { NLS_SWAP_IN; try { pimpl->xform_to(chem, basename, extension, allow_over, warn_over, pause, first_slice_size, slice_size, slice_perm, slice_user, slice_group, hash, min_digits, execute); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void libdar_xform::xform_to(int filedescriptor, const string & execute) { NLS_SWAP_IN; try { pimpl->xform_to(filedescriptor, execute); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } } // end of namespace dar-2.7.15/src/libdar/database.hpp0000644000175000017500000002307714636066467013631 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file database.hpp /// \brief this file holds the database class definition /// \ingroup API #ifndef DATABASE_HPP #define DATABASE_HPP #include "../my_config.h" #include #include "archive.hpp" #include "database_options.hpp" #include "database_archives.hpp" #include "archive_num.hpp" #include "database_listing_callback.hpp" namespace libdar { /// \addtogroup API /// @{ /// the database class defines the dar_manager database /// all operations for a dar_manager database are defines through the /// use of this class interface. This class also defines internally /// the data structure of the database. class database { public: /// this constructor build an empty database database(const std::shared_ptr & dialog); /// this constructor reads database from a file /// \param[in] dialog for user interaction /// \param[in] base database filename /// \param[in] opt extendable list of options to use for this operation database(const std::shared_ptr & dialog, const std::string & base, const database_open_options & opt); /// disabling copy constructor database(const database & ref) = delete; /// disabling move constructor database(database && ref) noexcept = delete; /// disabling assignement operator database & operator = (const database & ref) = delete; /// disabling move assignment operator database & operator = (database && ref) noexcept = delete; /// database destructor (no implicit file saving) ~database(); /// write the database to a file (see database_header first) /// \param[in] filename name of file to save database to /// \param[in] opt extendable list of options to use for this operation /// \note this method is not available with partially extracted databases. void dump(const std::string & filename, const database_dump_options & opt) const; // SETTINGS /// add an archive to the database /// \param[in] arch is the archive to add to the database (may be a partial archive) /// \param[in] chemin is the path to this archive to record in the database /// \param[in] basename is the archive's basename to record in the database /// \param[in] opt extendable list of options to use for this operation /// \note this method is not available with partially extracted databases. void add_archive(const archive & arch, const std::string & chemin, const std::string & basename, const database_add_options & opt); /// remove an archive from a database /// \param[in] min first archive index to remove /// \param[in] max last archive index to remove /// \param[in] opt extendable list of options to use for this operation /// \note the archives which indexes are in the range [min-max] are /// removed. If you want to remove only one archive choose min equal to max. /// \note this method is not available with partially extracted databases. void remove_archive(archive_num min, archive_num max, const database_remove_options & opt); /// change order of archive within the database /// \param[in] src archive index to move /// \param[in] dst archive index to move to /// \note this method is not available with partially extracted databases. void set_permutation(archive_num src, archive_num dst); /// change one's archive basename recorded in the database /// \param[in] num is the archive index to rename /// \param[in] basename is the new basename to give to that archive /// \param[in] opt optional parameters for this operation /// \note this method *is* available with partially extracted databases, but with partial_read_only ones void change_name(archive_num num, const std::string & basename, const database_change_basename_options &opt); /// change one's archive path recorded in the database /// \param[in] num is the archive index who's path to change /// \param[in] chemin is the new path to give to that archive /// \param[in] opt optional parameters for this operation /// \note this method *is* available with partially extracted databases, but with partial_read_only ones void set_path(archive_num num, const std::string & chemin, const database_change_path_options & opt); /// change the default options given to dar when performing restoration /// \param[in] opt is a vector a arguments. /// \note Each element of the vector must match a single argument of the command line /// like for example "-R". Any leading or trailing space will make a different argument /// than the one without spaces (" -R" is different than "-R" for example). /// \note this method *is* available with partially extracted databases, but with partial_read_only ones void set_options(const std::vector &opt); /// change the path to dar command /// \param[in] chemin is the full path to dar (including dar filename) to use for restoration /// \note if set to an empty string the dar command found from the PATH will be used (if any) /// \note this method *is* available with partially extracted databases, but with partial_read_only ones void set_dar_path(const std::string & chemin); /// change compression to use when storing base in file void set_compression(compression algozip) const; /// change the compression level to use when storing base in file void set_compression_level(U_I compr_level) const; // "GETTINGS" /// provides the list of archive used to build the database database_archives_list get_contents() const; /// return the options used with dar for restoration std::vector get_options() const; /// returns the path for dar /// \return the path to dar used when restoring files /// \note empty string means that dar is taken from the PATH variable std::string get_dar_path() const; /// returns the compression algorithm used on filesystem compression get_compression() const; /// returns the compression level used on file U_I get_compression_level() const; /// return the database format version std::string get_database_version() const; /// list files which are present in a given archive /// \param[in] callback is called to provide each entry in turn from the list /// \param[in] context is given as first argument of the callback as is provided here /// \param[in] num is the archive number to look at /// \param[in] opt optional parameters for this operation /// \note if "num" is set to zero all archive contents is listed /// \note this method is not available with partially extracted databases. void get_files(database_listing_show_files_callback callback, void *context, archive_num num, const database_used_options & opt) const; /// list the archive where a give file is present /// \param[in] callback is used to provide each entry in turn from the list /// \param[in] context is given as first argument of the callback as is provided here /// \param[in] chemin path to the file to look for /// \note this method is not available with partially extracted databases. void get_version(database_listing_get_version_callback callback, void *context, path chemin) const; /// compute some statistics about the location of most recent file versions /// \param[in] callback is used to provide each entry in turn from the list /// \param[in] context is given as first argument of the callback as is provided here /// \note this method is not available with partially extracted databases. void show_most_recent_stats(database_listing_statistics_callback callback, void *context) const; // "ACTIONS" (not available with partially extracted databases) /// restore files calling dar on the appropriated archive /// \param[in] filename list of filename to restore /// \param[in] opt extendable list of options to use for this operation /// \note this method is not available with partially extracted databases. void restore(const std::vector & filename, const database_restore_options & opt); /// check that all files's Data and EA are more recent when archive number grows within the database, only warn the user /// \return true if check succeeded, false if warning have been issued /// \note this method is not available with partially extracted databases. bool check_order() const; private: class i_database; std::unique_ptr pimpl; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/nls_swap.hpp0000644000175000017500000000333514636066467013706 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file nls_swap.hpp /// \brief provides a set of macro to change the NLS from user application domaine to libdar domain and viceversa /// \ingroup Private #ifndef NLS_SWAP_HPP #define NLS_SWAP_HPP #include "../my_config.h" #include #ifdef ENABLE_NLS /// \addtogroup Private /// @{ #define NLS_SWAP_IN \ std::string nls_swap_tmp; \ if(textdomain(nullptr) != nullptr) \ { \ nls_swap_tmp = textdomain(nullptr); \ textdomain(PACKAGE); \ } \ else \ nls_swap_tmp = "" #define NLS_SWAP_OUT \ if(nls_swap_tmp != "") \ textdomain(nls_swap_tmp.c_str()) /// @} #else #define NLS_SWAP_IN // #define NLS_SWAP_OUT // #endif #endif dar-2.7.15/src/libdar/compressor.cpp0000644000175000017500000002606014636066467014247 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { } // end extern "C" #include "tools.hpp" #include "compressor.hpp" #define BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFFER_SIZE #undef BUFFER_SIZE #define BUFFER_SIZE SSIZE_MAX #endif #endif using namespace std; namespace libdar { compressor::compressor(compression x_algo, generic_file & compressed_side, U_I compression_level) : proto_compressor(compressed_side.get_mode()) { wrapperlib_mode wr_mode; compr = nullptr; read_mode = (get_mode() == gf_read_only); compressed = & compressed_side; algo = x_algo; suspended = false; if(compression_level > 9) throw SRC_BUG; switch(algo) { case compression::none: return; // nothing mode to do! case compression::gzip: wr_mode = zlib_mode; break; case compression::bzip2: wr_mode = bzlib_mode; break; case compression::lzo: throw SRC_BUG; case compression::xz: wr_mode = xz_mode; break; case compression::lzo1x_1_15: throw SRC_BUG; case compression::lzo1x_1: throw SRC_BUG; case compression::zstd: throw SRC_BUG; case compression::lz4: throw SRC_BUG; default: throw SRC_BUG; } try { compr = new (nothrow) xfer(BUFFER_SIZE, wr_mode); if(compr == nullptr) throw Ememory("compressor::compressor"); if(! read_mode) { switch(compr->wrap.compressInit(compression_level)) { case WR_OK: compr->wrap.set_avail_out(0); break; case WR_MEM_ERROR: throw Ememory("compressor::compressor"); case WR_VERSION_ERROR: throw Erange("compressor::compressor", gettext("incompatible compression library version or unsupported feature required from compression library")); case WR_STREAM_ERROR: throw SRC_BUG; default: throw SRC_BUG; } } else { switch(compr->wrap.decompressInit()) { case WR_OK: compr->wrap.set_avail_in(0); break; case WR_MEM_ERROR: compr->wrap.decompressEnd(); throw Ememory("compressor::compressor"); case WR_VERSION_ERROR: compr->wrap.decompressEnd(); throw Erange("compressor::compressor", gettext("incompatible compression library version or unsupported feature required from compression library")); case WR_STREAM_ERROR: throw SRC_BUG; default: throw SRC_BUG; } } } catch(...) { if(compr != nullptr) delete compr; throw; } } compressor::~compressor() { try { terminate(); } catch(...) { // ignore all exceptions } if(compr != nullptr) delete compr; } compression compressor::get_algo() const { if(suspended) return compression::none; else return algo; } void compressor::suspend_compression() { if(!suspended) { inherited_sync_write(); inherited_flush_read(); suspended = true; } } void compressor::resume_compression() { if(suspended) suspended = false; } void compressor::inherited_truncate(const infinint & pos) { if(pos < get_position()) { inherited_sync_write(); inherited_flush_read(); } compressed->truncate(pos); } void compressor::inherited_terminate() { S_I ret; // flushing the pending data inherited_sync_write(); inherited_flush_read(); if(algo == compression::none) return; if(! read_mode) { ret = compr->wrap.compressEnd(); switch(ret) { case WR_OK: break; case WR_DATA_ERROR: // some data remains in the compression pipe (data loss) throw SRC_BUG; case WR_STREAM_ERROR: throw Erange("compressor::~compressor", gettext("compressed data is corrupted")); default : throw SRC_BUG; } } else { ret = compr->wrap.decompressEnd(); switch(ret) { case WR_OK: break; default: throw SRC_BUG; } } } U_I compressor::inherited_read(char *a, U_I size) { S_I ret; S_I flag = WR_NO_FLUSH; U_I mem_avail_out = 0; // will stop when avail_out will reach this value enum { normal, no_more_input, eof } processing = normal; if(size == 0) return 0; if(!read_mode) throw SRC_BUG; if(suspended || algo == compression::none) return compressed->read(a, size); compr->wrap.set_next_out(a); compr->wrap.set_avail_out(size); do { // feeding the input buffer if necessary if(compr->wrap.get_avail_in() == 0) { compr->wrap.set_next_in(compr->buffer); compr->wrap.set_avail_in(compressed->read(compr->buffer, compr->size)); if(compr->wrap.get_avail_in() == 0) mem_avail_out = compr->wrap.get_avail_out(); // could not add compressed data, so if no more clear data is produced // we must break the endless loop if WR_STREAM_END is not returned by compress() // this situation can occur upon data corruption else mem_avail_out = 0; // keep the target to fill the avail_out (avail_out == 0) } if(compr->wrap.get_avail_in() == 0) processing = no_more_input; ret = compr->wrap.decompress(flag); if(mem_avail_out == compr->wrap.get_avail_out() // nothing more extracted from compression engine && processing == no_more_input) // and no more compression data available processing = eof; switch(ret) { case WR_OK: case WR_STREAM_END: break; case WR_DATA_ERROR: throw Erange("compressor::gzip_read", gettext("compressed data CRC error")); case WR_MEM_ERROR: throw Ememory("compressor::gzip_read"); case WR_BUF_ERROR: // no process is possible: if(compr->wrap.get_avail_in() == 0) // because we reached EOF ret = WR_STREAM_END; // lib did not returned WR_STREAM_END (why ?) else // nothing explains why no process is possible: if(compr->wrap.get_avail_out() == 0) throw SRC_BUG; // bug from DAR: no output possible else throw SRC_BUG; // unexpected comportment from lib break; default: throw SRC_BUG; } } while(compr->wrap.get_avail_out() != mem_avail_out && ret != WR_STREAM_END && processing != eof); return compr->wrap.get_next_out() - a; } void compressor::inherited_write(const char *a, U_I size) { if(a == nullptr) throw SRC_BUG; if(size == 0) return; if(read_mode) throw SRC_BUG; if(suspended || algo == compression::none) compressed->write(a, size); else { compr->wrap.set_next_in(a); compr->wrap.set_avail_in(size); while(compr->wrap.get_avail_in() > 0) { // making room for output compr->wrap.set_next_out(compr->buffer); compr->wrap.set_avail_out(compr->size); switch(compr->wrap.compress(WR_NO_FLUSH)) { case WR_OK: case WR_STREAM_END: break; case WR_STREAM_ERROR: throw SRC_BUG; case WR_BUF_ERROR: throw SRC_BUG; default : throw SRC_BUG; } if(compr->wrap.get_next_out() != compr->buffer) { try { compressed->write(compr->buffer, (char *)compr->wrap.get_next_out() - compr->buffer); } catch(...) { // write failed we drop all // that could not be written // and propagate the exception flush_write(); throw; } } } } } void compressor::inherited_sync_write() { S_I ret; if(is_terminated()) throw SRC_BUG; if(read_mode || algo == compression::none) return; // nothing sync_write in read_mode or when no compression is performed if(compr->wrap.get_total_in() == 0) return; // sync write already done compr->wrap.set_avail_in(0); // no more input to add do { // setting the buffer for reception of data compr->wrap.set_next_out(compr->buffer); compr->wrap.set_avail_out(compr->size); ret = compr->wrap.compress(WR_FINISH); switch(ret) { case WR_OK: case WR_STREAM_END: if(compr->wrap.get_next_out() != compr->buffer) compressed->write(compr->buffer, (char *)compr->wrap.get_next_out() - compr->buffer); break; case WR_BUF_ERROR : throw SRC_BUG; case WR_STREAM_ERROR : throw SRC_BUG; default : throw SRC_BUG; } } while(ret != WR_STREAM_END); if(compr->wrap.compressReset() != WR_OK) throw SRC_BUG; } void compressor::inherited_flush_read() { if(is_terminated()) throw SRC_BUG; if(!read_mode || algo == compression::none) return; // nothing to flush read in write mode if(compr->wrap.decompressReset() != WR_OK) throw SRC_BUG; // keep in the buffer the bytes already read, these are discarded in case of a call to skip compr->wrap.set_avail_in(0); } void compressor::flush_write() { S_I ret; compr->wrap.set_avail_in(0); do { compr->wrap.set_next_out(compr->buffer); compr->wrap.set_avail_out(compr->size); ret = compr->wrap.compress(WR_FINISH); switch(ret) { case WR_OK: case WR_STREAM_END: // just ignore the data put by the compression engine // into compr->buffer break; case WR_BUF_ERROR : throw SRC_BUG; case WR_STREAM_ERROR : throw SRC_BUG; default : throw SRC_BUG; } } while(ret != WR_STREAM_END); if(compr->wrap.compressReset() != WR_OK) throw SRC_BUG; } //////////////////////// // xfer methods // // compressor::xfer::xfer(U_I sz, wrapperlib_mode mode) : wrap(mode) { buffer = new (nothrow) char[sz]; if(buffer == nullptr) throw Ememory("compressor::xfer::xfer"); size = sz; } compressor::xfer::~xfer() { if(buffer != nullptr) { delete [] buffer; buffer = nullptr; } } } // end of namespace dar-2.7.15/src/libdar/mem_ui.hpp0000644000175000017500000000732314636067146013327 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file mem_ui.hpp /// \brief class mem_ui definition. This class is to be used as parent class to handle user_interaction object management /// \ingroup Private #ifndef MEM_UI_HPP #define MEM_UI_HPP #include "../my_config.h" #include "user_interaction.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// class mem_ui to keep a copy of a user_interaction object /// this class is targeted for inheritance (it is advised to use a "protected" /// inheritance, not a "public" one). Instead of having all /// the stuf of managing, cloning, releasing a pointer on user_interaction /// a class simply put itslef as inherited from mem_ui to take the benefit /// of this implementation, once and for all. Use this class with caution /// espetially for class which will generate a ton of objects, as this will /// duplicate the user_interaction object in the same number. /// sometimes it is more efficient to have the user_interaction object as /// parameter of the constructor, using it if necessary while constructing the /// object only. In that situation, if the user_interaction is not need any /// further after construction, no need to make the class inherit from mem_ui. class mem_ui { public: /// constructor /// \param[in] dialog the user_interaction object to clone and store /// If you plan to use mem_ui, you should pass the user_interaction to its constructor /// for you later be able to call get_ui() at any time from the inherited class mem_ui(const std::shared_ptr & dialog); /// the copy constructor /// need to be called from the copy constructor of any inherited class that explicitely define one mem_ui(const mem_ui & ref) = default; /// the move constructor mem_ui(mem_ui && ref) noexcept = default; /// assignement operator /// you need to call it from the inherited class assignement operator /// if the inherited class explicitely defines its own one. mem_ui & operator = (const mem_ui & ref) = default; /// move operator mem_ui & operator = (mem_ui && ref) noexcept = default; /// destructor /// it is declared as virtual, for precaution, as it may not be very frequent to /// release an object having just a mem_ui pointer on it. virtual ~mem_ui() noexcept(false) {}; protected: /// get access to the user_interaction object /// \return a reference to the clone object. user_interaction & get_ui() const { return *ui; }; /// get access to the shared_ptr pointing to the user_interaction std::shared_ptr get_pointer() const { return ui; }; private: std::shared_ptr ui; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/header.hpp0000644000175000017500000001243514636066467013311 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file header.hpp /// \brief slice header structure is defined here /// \ingroup Private #ifndef HEADER_HPP #define HEADER_HPP #include "../my_config.h" #include "infinint.hpp" #include "generic_file.hpp" #include "user_interaction.hpp" #include "tlv_list.hpp" #include "label.hpp" namespace libdar { /// \addtogroup Private /// @{ const U_32 SAUV_MAGIC_NUMBER = 123; // Why "SAUV_..." because SAUV was the name of DAR much before its first release :-) using magic_number = U_32; enum flag_type { flag_type_terminal = 'T', flag_type_non_terminal = 'N', flag_type_located_at_end_of_slice = 'E' // since archive format version 8 }; /// this class manages the header of each slice /// this class was a struct before release 2.4.0, now promoted /// to a class it hides the fields and uses TLV to store the /// fields in the archive. This makes the header easier to /// extend by adding new fields, while letting a chance for the /// old implementation to be able to use more recent archives /// the main use of TLV is to handle optional fields easily. class header { public: // constructors & Co. header(); header(const header & ref) { copy_from(ref); }; header(header && ref) noexcept { nullifyptr(); move_from(std::move(ref)); }; header & operator = (const header & ref) { free_pointers(); copy_from(ref); return *this; }; header & operator = (header && ref) noexcept { move_from(std::move(ref)); return *this; }; ~header() { free_pointers(); }; // global methods void read(user_interaction & ui, generic_file & f, bool lax = false ); void write(user_interaction &, generic_file & f) const; /// minimal size of a header in an archive /// \return min size of a header once stored in an archive /// \note since release 2.4.0 the header used for each slice is exactly the same. /// before this release the header of the first slice might be bigger, it was known that /// the size of the other header was "min_size" this let dar be able to find the proper /// slice for a given position. For compatibility with older DAR format, it is thus important /// to not change the value returned by this class method. This call is only used /// when reading archive generated by old versions of dar < 2.4.0. (aka archive format <= 7) static U_I min_size() { return sizeof(magic_number) + label::common_size() + 2*sizeof(char); }; // fields access methods magic_number & get_set_magic() { return magic; }; label & get_set_internal_name() { return internal_name; }; char & get_set_flag() { return flag; }; label & get_set_data_name() { return data_name; }; bool get_first_slice_size(infinint & size) const; void set_first_slice_size(const infinint & size); void unset_first_slice_size() { if(first_size != nullptr) { delete first_size; first_size = nullptr; } }; bool get_slice_size(infinint & size) const; void set_slice_size(const infinint & size); void unset_slice_size() { if(slice_size != nullptr) { delete slice_size; slice_size = nullptr; } }; bool is_old_header() const { return old_header; }; void set_format_07_compatibility() { old_header = true; }; private: magic_number magic; ///< constant string for all Dar archives label internal_name; ///< constant string for all slices of a given archive (computed based on date and pid) label data_name; ///< constant string for a set of data (constant with dar_xform, used to link isolated catalogue to its original data) char flag; ///< whether slice is the last of the archive or not infinint *first_size; ///< size of the first slice infinint *slice_size; ///< size of slices (except first slice if specified else and last if not fulfilled) bool old_header; ///< true if the header has been read from an old archive (before release 2.4.0, format 07 and below) and if true when writing, create an old slice header (compatible with format 07). void nullifyptr() noexcept { first_size = slice_size = nullptr; }; void copy_from(const header & ref); void move_from(header && ref) noexcept; void free_pointers(); void fill_from(user_interaction & ui, const tlv_list & list); tlv_list build_tlv_list(user_interaction & ui) const; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/datetime.hpp0000644000175000017500000001522014636067146013643 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file datetime.hpp /// \brief this file contains the definition of class datetime that stores unix times in a portable way /// \ingroup API #ifndef DATETIME_HPP #define DATETIME_HPP extern "C" { #if HAVE_SYS_TYPES_H #include #endif #if HAVE_UTIME_H #include #endif #if HAVE_SYS_TIME_H #include #endif } // end extern "C" #include "../my_config.h" #include "infinint.hpp" namespace libdar { /// \addtogroup API /// @{ /// no need to dig into these classes from the API class archive_version; class generic_file; /// stores time information class datetime { public: // time units must be sorted: the first is the smallest step, last is the largest increment. // this makes the comparison operators (<, >, <=, >=,...) become naturally defined on that type enum time_unit { tu_nanosecond, tu_microsecond, tu_second }; /// constructor based on the number of second ellasped since the end of 1969 datetime(const infinint & value = 0) { val = value; uni = tu_second; }; /// general constructor /// \param[in] second the number of second since the dawn of computer time (1970) /// \param[in] subsec the fraction of the time below 1 second expressed in the time unit given as next argument /// \param[in] unit the time unit in which is expressed the previous argument datetime(time_t second, time_t subsec, time_unit unit); /// constructor reading data dump() into a generic_file datetime(generic_file &x, archive_version ver); datetime(const datetime & ref) = default; datetime(datetime && ref) noexcept = default; datetime & operator = (const datetime & ref) = default; datetime & operator = (datetime && ref) noexcept = default; ~datetime() = default; // comparison operators bool operator < (const datetime & ref) const; bool operator == (const datetime & ref) const; bool operator != (const datetime & ref) const { return ! (*this == ref); }; bool operator >= (const datetime & ref) const { return ! (*this < ref); }; bool operator > (const datetime & ref) const { return ref < *this; }; bool operator <= (const datetime & ref) const { return ref >= *this; }; // arithmetic on time void operator -= (const datetime & ref); void operator += (const datetime & ref); datetime operator - (const datetime & ref) const { datetime tmp(*this); tmp -= ref; return tmp; }; datetime operator + (const datetime & ref) const { datetime tmp(*this); tmp += ref; return tmp; }; /// equivalent to operator == but if compared object use different time unit, do the comparison rounding up the values to the largest unit bool loose_equal(const datetime & ref) const; /// at the difference of operator - provides the difference using the less precise unit used between the two elements datetime loose_diff(const datetime & ref) const; /// return the integer number of second infinint get_second_value() const { infinint sec, sub; get_value(sec, sub, uni); return sec; }; /// return the subsecond time fraction expressed in the given time unit infinint get_subsecond_value(time_unit unit) const; /// returns the time unit used internally to store the subsecond time fraction time_unit get_unit() const { return uni; }; /// return a time as time_t arguments /// \param[out] second the time value in second /// \param[out] subsecond is the remaining time fraction as expressed in the unit given as next argument /// \param[in] unit the unit of the subsecond fraction of the timestamp /// \return true upon success, false if the value cannot be represented by system types (overflow) bool get_value(time_t & second, time_t & subsecond, time_unit unit) const; /// write down this to file void dump(generic_file &x) const; /// read this from file void read(generic_file &f, archive_version ver); /// return true if the datetime is exactly January 1st, 1970, 0 h 0 mn 0 s bool is_null() const { return val.is_zero(); }; /// return true if the datetime is an integer number of second (subsecond part is zero) bool is_integer_second() const { return (uni == tu_second); }; /// return the storage it would require to dump this object infinint get_storage_size() const; /// set to null (zero) void nullify() { val = 0; uni = tu_second ; }; private: // the date must not be stored as a single integer // to avoid reducing the possible addressable dates // when compiling using 32 or 64 bits integer in place // of infinint. The fraction cannot handle smaller unit // than nanosecond if using 32 bits integer. infinint val; //< the date expressed in the "uni" time unit time_unit uni; //< the time unit used to store the subsecond fraction of the timestamp. /// reduce the value to the largest unit possible void reduce_to_largest_unit() const; void get_value(infinint & sec, infinint & sub, time_unit unit) const; void build(const infinint & sec, const infinint & sub, time_unit unit); static time_unit min(time_unit a, time_unit b); static time_unit max(time_unit a, time_unit b); static char time_unit_to_char(time_unit a); static time_unit char_to_time_unit(const char a); /// return the factor between two units /// \note "source" must be larger than "dest" (source >= dest), else an exception is thrown /// \return the factor f, which makes the following to be true: source = f*dest static const infinint & get_scaling_factor(time_unit source, time_unit dest); }; /// converts dar_manager database version to dar archive version in order to properly read time fields extern archive_version db2archive_version(unsigned char db_version); /// @} } // end of namespace #endif dar-2.7.15/src/libdar/label.cpp0000644000175000017500000000633414636066467013134 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_STRING_H #include #endif } // end extern "C" #include "label.hpp" #include "infinint.hpp" #include "tools.hpp" namespace libdar { label::label() { clear(); } bool label::operator == (const label & ref) const { return memcmp(val, ref.val, LABEL_SIZE) == 0; } void label::clear() { (void)memset(val, 0, LABEL_SIZE); } bool label::is_cleared() const { U_I i = 0; while(i < LABEL_SIZE && val[i] == '\0') i++; return i >= LABEL_SIZE; } void label::generate_internal_filename() { const time_t src1 = ::time(nullptr); const pid_t src2 = getpid(); const uid_t src3 = getuid(); unsigned char *dest = (unsigned char *)(&val); unsigned char *src = (unsigned char *)(&src1); U_I s1 = sizeof(src1) < LABEL_SIZE ? sizeof(src1) : LABEL_SIZE; for(U_I i = 0 ; i < s1; ++i) dest[i] = src[i]; if(s1 < LABEL_SIZE) { s1 = LABEL_SIZE - s1; // number of byte left to fill in "ret" s1 = s1 < sizeof(src2) ? s1 : sizeof(src2); // number of byte to copy src = (unsigned char *)(&src2); for(U_I i = 0; i < s1; ++i) dest[sizeof(src1)+i] = src[i]; } s1 = sizeof(src1) + sizeof(src2); if(s1 < LABEL_SIZE) { U_I s2 = LABEL_SIZE - s1; s2 = s2 < sizeof(src3) ? s2 : sizeof(src3); src = (unsigned char *)(&src3); for(U_I i = 0; i < s2; ++i) dest[s1+i] = src[i]; } for(s1 = s1 + sizeof(src3); s1 < LABEL_SIZE; ++s1) dest[s1] = (U_I)tools_pseudo_random(255); } void label::read(generic_file & f) { if(f.read(val, LABEL_SIZE) != (S_I)LABEL_SIZE) throw Erange("label::read", gettext("Incomplete label")); } void label::dump(generic_file & f) const { f.write(val, LABEL_SIZE); } void label::copy_from(const label & ref) { (void)memcpy(val, ref.val, LABEL_SIZE); } void label::move_from(label && ref) noexcept { std::swap(val, ref.val); } const label label_zero; } // end of namespace dar-2.7.15/src/libdar/fichier_global.cpp0000644000175000017500000000552114636066467015003 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_LIMITS_H #include #endif } // end extern "C" #include "infinint.hpp" #include "generic_file.hpp" #include "erreurs.hpp" #include "tools.hpp" #include "cygwin_adapt.hpp" #include "int_tools.hpp" #include "fichier_global.hpp" #include "tools.hpp" #include #include #define BUFFER_SIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFFER_SIZE #undef BUFFER_SIZE #define BUFFER_SIZE SSIZE_MAX #endif #endif using namespace std; namespace libdar { void fichier_global::inherited_write(const char *a, U_I size) { U_I wrote = 0; while(wrote < size && !disk_full) { wrote += fichier_global_inherited_write(a+wrote, size-wrote); if(wrote < size) { try { get_ui().pause(gettext("No space left on device, you have the opportunity to make room now. When ready : can we continue ?")); } catch(Euser_abort & e) { disk_full = true; throw; } } } } U_I fichier_global::inherited_read(char *a, U_I size) { U_I ret = 0; U_I read = 0; string message; while(!fichier_global_inherited_read(a+ret, size-ret, read, message)) { ret += read; get_ui().pause(message); } ret += read; return ret; } } // end of namespace dar-2.7.15/src/libdar/libdar.pc.tmpl.in0000644000175000017500000000234514636066467014510 00000000000000####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libdar Description: Disk ARchive library Requires: Version: #LIBDAR_VERSION# Libs: -L${libdir} -ldar#LIBDAR_SUFFIX# @LIBS@ Cflags: -I${includedir} #LIBDAR_MODE# #CXXSTDFLAGS# dar-2.7.15/src/libdar/user_interaction_callback.hpp0000644000175000017500000001231214636066467017244 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file user_interaction_callback.hpp /// \brief defines the interaction between libdar and the user based on callback functions /// \ingroup API /// /// Three classes are defined /// - user_interaction is the root class that you can use to make your own classes /// - user_interaction_callback is a specialized inherited class which is implements /// user interaction thanks to callback functions /// - user_interaction_blind provides fully usable objects that do not show anything /// and always assume a negative answer from the user /// . #ifndef USER_INTERACTION_CALLBACK_HPP #define USER_INTERACTION_CALLBACK_HPP #include "../my_config.h" #include #include "user_interaction.hpp" #include "secu_string.hpp" namespace libdar { /// \addtogroup API /// @{ /// full implemented class for user_interaction based on callback functions. /// this class is an inherited class of user_interaction it is used by /// dar command line programs, but you can use it if you wish. /// \ingroup API class user_interaction_callback : public user_interaction { public: using message_callback = void (*)(const std::string &x, void *context); using pause_callback = bool (*)(const std::string &x, void *context); using get_string_callback = std::string (*)(const std::string &x, bool echo, void *context); using get_secu_string_callback = secu_string (*)(const std::string &x, bool echo, void *context); /// constructor which receive the callback functions. /// \param[in] x_message_callback is used by message() method /// \param[in] x_answer_callback is used by the pause() method /// \param[in] x_string_callback is used by get_string() method /// \param[in] x_secu_string_callback is used by get_secu_string() method /// \param[in] context_value will be passed as last argument of callbacks when /// called from this object. /// \note The context argument of each callback is set with the context_value given /// in the user_interaction_callback object constructor. The value can /// can be any arbitrary value (nullptr is valid), and can be used as you wish. /// Note that the listing callback is not defined here, but thanks to a specific method user_interaction_callback(message_callback x_message_callback, pause_callback x_answer_callback, get_string_callback x_string_callback, get_secu_string_callback x_secu_string_callback, void *context_value); user_interaction_callback(const user_interaction_callback & ref) = default; user_interaction_callback(user_interaction_callback && ref) noexcept = default; user_interaction_callback & operator = (const user_interaction_callback & ref) = default; user_interaction_callback & operator = (user_interaction_callback && ref) noexcept = default; ~user_interaction_callback() = default; /// listing callback can be now passed directly to archive::get_children_of() /// dar_manager_show_files callback can now be passed directly to database::get_files() /// dar_manager_contents callback is not necessary, use database::get_contents() method /// dar_manager_statistics callback can now be passed directly to database::show_most_recent_stats() /// dar_manager_get_show_version callback can now be passed directly to database::get_version() protected: /// overwritting method from parent class. virtual void inherited_message(const std::string & message) override; /// overwritting method from parent class. virtual bool inherited_pause(const std::string & message) override; /// overwritting method from parent class. virtual std::string inherited_get_string(const std::string & message, bool echo) override; /// overwritting method from parent class. virtual secu_string inherited_get_secu_string(const std::string & message, bool echo) override; /// change the context value of the object that will be given to callback functions void change_context_value(void *new_value) { context_val = new_value; }; private: message_callback message_cb; pause_callback pause_cb; get_string_callback get_string_cb; get_secu_string_callback get_secu_string_cb; void *context_val; }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/database_archives.hpp0000644000175000017500000000514714636066467015513 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file database_archives.hpp /// \brief this file stores the list of archives a database has been built from. /// it is part of the database header /// \ingroup API #ifndef DATABASE_ARCHIVE_HPP #define DATABASE_ARCHIVE_HPP #include "../my_config.h" #include #include namespace libdar { /// \addtogroup API /// @{ /// datastructure managing a member of the list of archives used by a database /// only two methods are useful from API point of view /// - get_path() /// - get_basename() class database_archives { public: database_archives() {}; // fields "chemin" and "base" are objects and get initialized by the std::string default constructor database_archives(const database_archives & ref) = default; database_archives(database_archives && ref) noexcept = default; database_archives & operator = (const database_archives & ref) = default; database_archives & operator = (database_archives && ref) noexcept = default; ~database_archives() = default; void set_path(const std::string & val) { chemin = val; }; void set_basename(const std::string & val) { base = val; }; /// this provides the path where is located this archive const std::string & get_path() const { return chemin; }; /// this provides the basename of the archive const std::string & get_basename() const { return base; }; private: std::string chemin; std::string base; }; /// list of archives found in a database /// \note index 0 is not used, list starts at index 1 using database_archives_list = std::deque; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/parallel_block_compressor.hpp0000644000175000017500000003315614636067146017301 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file parallel_block_compressor.hpp /// \brief provide per block and parallel compression/decompression /// \ingroup Private /// Several classes are defined here: /// - class parallel_block_compressor, which has similar interface and behavior as class compressor but compresses a /// data of a file per block of given size which allows parallel compression of a given file's data at the /// cost of memory requirement and probably a less good compression ratio (depends on the size of the blocks) /// - class zip_below_write which gather the compressed data works of workers toward the filesystem /// - class zip_below_read which provides block of compressed data to workers comming from the filesystem /// - class zip_worker which instanciates worker objects to compute in parallel the compression of block of data /// . /// /// the compressed block structure is a sequence of 2 types of data /// - data to compress/uncompress /// - eof /// . /// /// COMPRESSION PROCESS /// /// *** in the archive: /// /// the block stream is an arbitrary length sequence of data blocks followed by an eof. /// Each data block consists of an infinint determining the length of following field /// that stores the compressed data of variable length /// the compressed data comes from block of uncompressed data of constant size (except the /// last one that is smaller. THis information is needed to prepare memory allocation /// and is stored in the archive header (archive reading) or provided by the user /// (archive creation). Providing 0 for the block size leads to the classical/historical /// but impossible to parallelize compression algorithm (gzip, bzip2, etc.) /// /// /// /// *** between threads: /// /// when compressing, the inherited_write call produces a set of data block to the ratelier_scatter /// treated by a set of zip_workers which in turn passe the resulting compressed /// data blocks to the zip_below_write thread. /// /// last when eof is reached or during terminate() method, N eof blocks are sent /// where N is the number of zip_workers, either with flag eof, or die (termination). /// the eof flag is just passed by the workers but the zip_below_write thread collects /// them and awakes the main thread once all are collected and thus all data written. /// the die flag drives the thread termination, worker first pass them to the /// zip_below_thread which collect them and terminates too. /// /// blocks used are of type crypto_segment the clear_data containes the uncompressed /// data, the crypted_data block contains the compressed data. The block_index is not used. /// /// upon receiption of data blocks, each zip_workers read the clear_data and produce the /// compressed data to the crypted_data mem_block and pushes that to the ratelier_gather /// which passed to and write down by the zip_below_write. /// /// /// /// UNCOMPRESSON PROCESS /// /// the zip_below_read expectes to find the clear_block size at construction time and /// provides a method to read it by the parallel_block_compressor thread which allocate the /// mem_blocks accordingly in a pool (same as parallel_tronconneuse). The zip_below /// thread once started reading the blocks and push them into the ratelier_scatter /// (without the initial U_32 telling the size of the compressed data that follows). /// Upon error (incoherent strucuture ....) the thread pushes N error block in the /// ratelier_scatter and terminates /// /// the zip_workers fetch a block and put the corresponding uncompressed data to the /// clear_data mem_block then push the crypto_segment to the ratelier_gather. Upon /// uncompression error, the error flag is set with the crypto_segment, the zip_worker /// continues to work anyway until it reads a crypto_segment with the eof_die flag from /// the ratelier_scatter. /// Upon reception of the error flag from the ratelier_gather the parallel_compression /// thread invoke a method of the zip_below_read that triggers the thread to terminate /// after having pushed N error blocks to the ratelier_scatter which in turns triggers /// the termination of the zip_workers. The parallel_block_compressor thread can then gather /// the N error block from the ratelier_gather, join() the threads and report the /// compression error #ifndef PARALLEL_BLOCK_COMPRESSOR_HPP #define PARALLEL_BLOCK_COMPRESSOR_HPP #include "../my_config.h" #include "infinint.hpp" #include "crypto_segment.hpp" #include "heap.hpp" #include "compress_module.hpp" #include "proto_compressor.hpp" #include namespace libdar { /// \addtogroup Private /// @{ /// the different flags used to communicate between threads hold by parallel_block_compressor class enum class compressor_block_flags { data = 0, eof_die = 1, error = 2, worker_error = 3 }; // the following classes hold the subthreads of class parallel_block_compressor // and are defined just after it below class zip_below_read; class zip_below_write; class zip_worker; ///////////////////////////////////////////////////// // // paralle_compressor class, which holds the sub-threads // // class parallel_block_compressor: public proto_compressor { public: /// \note if uncompressed_bs is too small the uncompressed data /// will not be able to be stored in the datastructure and decompression /// wil fail. This metadata should be stored in the archive header /// and passed to the parallel_block_compressor object at /// construction time both while reading and writing an archive parallel_block_compressor(U_I num_workers, std::unique_ptr block_zipper, generic_file & compressed_side, U_I uncompressed_bs = default_uncompressed_block_size); // compressed_side is not owned by the object and will remains // after the objet destruction parallel_block_compressor(const parallel_block_compressor & ref) = delete; parallel_block_compressor(parallel_block_compressor && ref) noexcept = delete; parallel_block_compressor & operator = (const parallel_block_compressor & ref) = delete; parallel_block_compressor & operator = (parallel_block_compressor && ref) noexcept = delete; ~parallel_block_compressor(); // inherited from proto_compressor virtual compression get_algo() const override { return suspended? compression::none : zipper->get_algo(); }; virtual void suspend_compression() override; virtual void resume_compression() override; virtual bool is_compression_suspended() const override { return suspended; }; // inherited from generic file virtual bool skippable(skippability direction, const infinint & amount) override; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override; virtual infinint get_position() const override; protected : virtual void inherited_read_ahead(const infinint & amount) override { if(!suspended) run_read_threads(); }; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, U_I size) override; virtual void inherited_truncate(const infinint & pos) override; virtual void inherited_sync_write() override; virtual void inherited_flush_read() override { stop_read_threads(); }; virtual void inherited_terminate() override; private: // the local fields U_I num_w; ///< number of worker threads std::unique_ptr zipper; ///< compress_module for the requested compression algo generic_file *compressed; ///< where to read from / write to, compressed data U_I uncompressed_block_size; ///< the max block size of un compressed data used bool suspended; ///< whether compression is suspended or not bool running_threads; ///< whether subthreads are running std::unique_ptr curwrite; ///< aggregates a block before compression and writing std::deque > lus_data; ///< uncompressed data from workers in read mode std::deque lus_flags; ///< uncompressed data flags from workers in read mode bool reof; ///< whether we have hit the end of file while reading // inter-thread data structure std::shared_ptr > disperse; std::shared_ptr > rassemble; std::shared_ptr > tas; // the subthreads std::unique_ptr reader; std::unique_ptr writer; std::deque > travailleurs; // private methods void send_flag_to_workers(compressor_block_flags flag); void stop_threads(); void stop_read_threads(); void stop_write_threads(); void run_threads(); void run_read_threads(); void run_write_threads(); compressor_block_flags purge_ratelier_up_to_non_data(); // static methods static U_I get_ratelier_size(U_I num_workers) { return num_workers + num_workers/2; }; static U_I get_heap_size(U_I num_workers); }; ///////////////////////////////////////////////////// // // zip_below_write class/sub-thread // // class zip_below_write: public libthreadar::thread { public: zip_below_write(const std::shared_ptr > & source, generic_file *dest, const std::shared_ptr > & xtas, U_I num_workers); ~zip_below_write() { kill(); join(); }; /// consulted by the main thread, set to true by the zip_below_write thread /// when an exception has been caught or an error flag has been seen from a /// worker bool exception_pending() const { return error; }; /// reset the thread objet ready for a new compression run, but does not launch it void reset(); protected: virtual void inherited_run() override; private: std::shared_ptr > src; generic_file *dst; std::shared_ptr > tas; U_I num_w; bool error; U_I ending; std::deque > data; std::deque flags; libthreadar::mutex get_pos; ///< to manage concurrent access to current_position infinint current_position; void work(); void pop_front() { tas->put(std::move(data.front())); data.pop_front(); flags.pop_front(); }; }; ///////////////////////////////////////////////////// // // zip_below_read class/sub-thread // // class zip_below_read: public libthreadar::thread { public: zip_below_read(generic_file *source, const std::shared_ptr > & dest, const std::shared_ptr > & xtas, U_I num_workers); ~zip_below_read() { kill(); join(); }; /// this will trigger the sending of N eof_die blocks and thread termination void do_stop() { should_i_stop = true; }; /// will read the uncompr block from the source generic_file but will not launch the thread void reset(); protected: virtual void inherited_run() override; private: generic_file *src; const std::shared_ptr > & dst; const std::shared_ptr > & tas; U_I num_w; std::unique_ptr ptr; bool should_i_stop; void work(); void push_flag_to_all_workers(compressor_block_flags flag); }; ///////////////////////////////////////////////////// // // zip_worker class/sub-thread // // class zip_worker: public libthreadar::thread { public: zip_worker(std::shared_ptr > & read_side, std::shared_ptr > & write_size, std::unique_ptr && ptr, bool compress); ~zip_worker() { kill(); join(); }; protected: virtual void inherited_run() override; private: std::shared_ptr > & reader; std::shared_ptr > & writer; std::unique_ptr compr; bool do_compress; bool error; std::unique_ptr transit; unsigned int transit_slot; void work(); }; /// @} } // end of namespace #endif dar-2.7.15/src/libdar/mycurl_easyhandle_sharing.cpp0000644000175000017500000000347414636066467017302 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "mycurl_easyhandle_sharing.hpp" using namespace std; namespace libdar { #if LIBCURL_AVAILABLE shared_ptr mycurl_easyhandle_sharing::alloc_instance() { shared_ptr ret; deque >::const_iterator it = table.begin(); while(it != table.end() && it->use_count() > 1) ++it; if(it != table.end()) ret = *it; else { try { table.push_back(make_shared()); if(table.back().use_count() != 1) throw SRC_BUG; else ret = table.back(); } catch(bad_alloc & e) { throw Ememory("mycurl_easyhandle_sharing::alloc_instance"); } } ret->setopt_all_default(); ret->setopt_list(global_params); return ret; } #endif } // end of namespace dar-2.7.15/src/README0000644000175000017500000000304514636066467010770 00000000000000What will you find here? Source code for dar suite program. build directory contains source code that if put in their expected place are modified by the automake/autoconf tool, thing that would leading to mix generated code withing source code. see misc/init to set these files in place automatically. libdar directory contains all code of dar that have been moved since version 2.0.0 to a separated library known as libdar. dar_suite directory contains the command-line specific code to build dar, dar_xform, dar_slave dar_cp, dar_manager and dar_split on top of libdar examples directory contains few programs that illustrate the use of infinint, they are built when using --enable-examples with the configure script. testing directory contains a plethora of very simple program that helped validate each C++ module of dar. They are to be used in step by step debugging and in conjunction with variable watch. Theses programs are intended to be used only by developers, they are built when using --enable-examples with the configure script. check directory contains the global validation routine that are run when calling "make check". Note that running 'make check' may take several weeks to complete depending on the power of your computer, this is also due to the fact that these routine are very basic but robust, they are not especially optimized for speed. python directory contains the python3 binding for libdar, it is built with the rest of the source code if prerequisites are found by the ./configure script.dar-2.7.15/src/Makefile.in0000644000175000017500000005107114640025155012137 00000000000000# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(dist_noinst_DATA) \ $(noinst_HEADERS) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac DATA = $(dist_noinst_DATA) HEADERS = $(noinst_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` DIST_SUBDIRS = libdar dar_suite check python examples testing am__DIST_COMMON = $(srcdir)/Makefile.in README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CXXSTDFLAGS = @CXXSTDFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN_PROG = @DOXYGEN_PROG@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GPGME_CFLAGS = @GPGME_CFLAGS@ GPGME_CONFIG = @GPGME_CONFIG@ GPGME_LIBS = @GPGME_LIBS@ GPGRT_CONFIG = @GPGRT_CONFIG@ GREP = @GREP@ HAS_DOT = @HAS_DOT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBCURL_CFLAGS = @LIBCURL_CFLAGS@ LIBCURL_LIBS = @LIBCURL_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTHREADAR_CFLAGS = @LIBTHREADAR_CFLAGS@ LIBTHREADAR_LIBS = @LIBTHREADAR_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGMERGE = @MSGMERGE@ MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POSUB = @POSUB@ PYEXT = @PYEXT@ PYFLAGS = @PYFLAGS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ UPX_PROG = @UPX_PROG@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dot = @dot@ doxygen = @doxygen@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ groff = @groff@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ tmp = @tmp@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ upx = @upx@ @MAKE_ALL_DIR_TRUE@OPT_SUBDIR = examples testing SUBDIRS = libdar dar_suite check python $(OPT_SUBDIR) noinst_HEADERS = my_config.h dist_noinst_DATA = README gettext.h all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(DATA) $(HEADERS) installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dar-2.7.15/src/Makefile.am0000644000175000017500000000221514636066467012142 00000000000000####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### if MAKE_ALL_DIR OPT_SUBDIR = examples testing endif SUBDIRS = libdar dar_suite check python $(OPT_SUBDIR) noinst_HEADERS = my_config.h dist_noinst_DATA = README gettext.h dar-2.7.15/src/my_config.h0000644000175000017500000000253414636066467012235 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file my_config.h /// \brief include macro defined by the configure script and some specific additional ones /// \ingroup Private #ifndef MY_CONFIG_H #define MY_CONFIG_H #if HAVE_CONFIG_H #define NODUMP_LINUX 0 #define NODUMP_EXT2FS 1 #include "config.h" // workaround for autoconf 2.59 #undef mbstate_t #endif #include "gettext.h" #endif dar-2.7.15/src/dar_suite/0000755000175000017500000000000014640025216012123 500000000000000dar-2.7.15/src/dar_suite/no_comment.cpp0000644000175000017500000000610014636066467014724 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "no_comment.hpp" using namespace libdar; void no_comment::fill_morceau() { partie tmp; infinint last_offset = 0; char a = ' '; enum { st_unknown, st_command, st_comment } status = st_unknown; bool stop = false; bool last_block_is_comment = true; morceau.clear(); if(ref == nullptr) throw SRC_BUG; ref->skip(0); tmp.longueur = 0; while(!stop) { stop = ref->read(&a, 1) != 1; switch(status) { case st_unknown: switch(a) { case ' ': case '\t': ++tmp.longueur; break; case '\n': // cannot treat empty lines as command, because too short to fit in loop/switch tmp.longueur = 0; break; case '#': status = st_comment; break; default: status = st_command; tmp.debut = ref->get_position() - 1; tmp.offset = last_offset; tmp.longueur = 1; } break; case st_comment: if(a == '\n') { status = st_unknown; last_block_is_comment = true; tmp.longueur = 0; } break; case st_command: if(!stop) ++tmp.longueur; if(a == '\n' || stop) { status = st_unknown; if(last_block_is_comment) { morceau.push_back(tmp); last_offset = tmp.offset+tmp.longueur; } else { if(morceau.empty()) throw SRC_BUG; morceau.back().longueur = ref->get_position() - morceau.back().debut; last_offset = morceau.back().offset+morceau.back().longueur; } last_block_is_comment = false; tmp.longueur = 0; } break; default: throw SRC_BUG; } } } dar-2.7.15/src/dar_suite/dar_slave.cpp0000644000175000017500000002161214636066467014533 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #include "getopt_decision.h" } // end extern "C" #include #include #include #include "tools.hpp" #include "dar_suite.hpp" #include "libdar.hpp" #include "line_tools.hpp" // to be removed when dar_slave will be part of libdar #include "tuyau.hpp" #include "sar.hpp" #include "slave_zapette.hpp" #define ONLY_ONCE "Only one -%c is allowed, ignoring this extra option" #define OPT_STRING "i:o:hVE:Qj9:" using namespace libdar; using namespace std; #define DAR_SLAVE_VERSION "1.6.0" static bool command_line(shell_interaction & dialog, S_I argc, char * const argv[], string & chemin, string & filename, string & input_pipe, string & output_pipe, string & execute, infinint & min_digits); static void show_usage(shell_interaction & dialog, const char *command); static void show_version(shell_interaction & dialog, const char *command); static S_I little_main(shared_ptr & dialog, S_I argc, char * const argv[], const char **env); int main(S_I argc, char * const argv[], const char **env) { return dar_suite_global(argc, argv, env, OPT_STRING, #if HAVE_GETOPT_LONG nullptr, #endif '\0', // should never be met as option, thus early read the whole command-line for -j and -Q options &little_main); } static S_I little_main(shared_ptr & dialog, S_I argc, char * const argv[], const char **env) { string chemin; string filename; string input_pipe; string output_pipe; string execute; infinint min_digits; shell_interaction *ptr = dynamic_cast(dialog.get()); if(!dialog) throw SRC_BUG; if(ptr == nullptr) throw SRC_BUG; if(command_line(*ptr, argc, argv, chemin, filename, input_pipe, output_pipe, execute, min_digits)) { line_tools_check_basename(*dialog, chemin, filename, EXTENSION, false); if(min_digits.is_zero()) line_tools_check_min_digits(*dialog, chemin, filename, EXTENSION, min_digits); libdar::libdar_slave slave(dialog, chemin, filename, EXTENSION, input_pipe.size() == 0, input_pipe.size() == 0 ? "0" : input_pipe, output_pipe.size() == 0, output_pipe.size() == 0 ? "1" : output_pipe, execute, min_digits); slave.run(); return EXIT_OK; } else return EXIT_SYNTAX; } static bool command_line(shell_interaction & dialog, S_I argc, char* const argv[], string & chemin, string & filename, string & input_pipe, string & output_pipe, string & execute, infinint & min_digits) { S_I lu; execute = ""; path *tmp = nullptr; if(argc < 1) { dialog.message(gettext("Cannot read arguments on command line, aborting")); return false; } while((lu = getopt(argc, argv, OPT_STRING)) != EOF) { switch(lu) { case 'i': if(optarg == nullptr) throw Erange("command_line", gettext("Missing argument to -i option")); if(input_pipe == "") input_pipe = optarg; else dialog.message(tools_printf(gettext(ONLY_ONCE), char(lu))); break; case 'o': if(optarg == nullptr) throw Erange("command_line", gettext("Missing argument to -o option")); if(output_pipe == "") output_pipe = optarg; else dialog.message(tools_printf(gettext(ONLY_ONCE), char(lu))); break; case 'h': show_usage(dialog, argv[0]); return false; case 'V': show_version(dialog, argv[0]); return false; case 'E': if(optarg == nullptr) throw Erange("command_line", gettext("Missing argument to -E option")); if(execute == "") execute = optarg; else execute += string(" ; ") + optarg; break; case 'Q': break; // ignore this option already parsed during initialization (dar_suite.cpp) case '9': if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext("Missing argument to --min-digits"), char(lu))); else { infinint tmp2, tmp3; line_tools_get_min_digits(optarg, min_digits, tmp2, tmp3); } break; case ':': throw Erange("command_line", tools_printf(gettext("Missing parameter to option -%c"), char(optopt))); case '?': throw Erange("command_line", tools_printf(gettext("Ignoring unknown option -%c"), char(optopt))); default: throw Erange("command_line", tools_printf(gettext("Ignoring unknown option -%c"), char(lu))); } } if(optind + 1 > argc) { dialog.message(gettext("Missing archive basename, see -h option for help")); return false; } if(optind + 1 < argc) { dialog.message(gettext("Too many argument on command line, see -h option for help")); return false; } try { line_tools_split_path_basename(argv[optind], tmp, filename); if(tmp == nullptr) throw SRC_BUG; chemin = tmp->display(); } catch(...) { if(tmp != nullptr) { delete tmp; tmp = nullptr; } throw; } if(tmp != nullptr) { delete tmp; tmp = nullptr; } return true; } static void show_usage(shell_interaction & dialog, const char *command) { string cmd; line_tools_extract_basename(command, cmd); dialog.change_non_interactive_output(cout); dialog.printf("\nusage : \n"); dialog.printf(" command1 | %s [options] [/]basename | command2\n", cmd.c_str()); dialog.printf(" %s [options] [-i input_pipe] [-o output_pipe] [/]basename\n", cmd.c_str()); dialog.printf(" %s -h\n", cmd.c_str()); dialog.printf(" %s -V\n\n", cmd.c_str()); dialog.printf(gettext("\n")); dialog.printf(gettext("Common options:\n")); dialog.printf(gettext(" -i pipe to use instead of std input to read orders from dar\n")); dialog.printf(gettext(" -o pipe to use instead of std output to write data to dar\n")); dialog.printf(gettext(" -E \t command line to execute between slices of the archive\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("See man page for more options.\n")); } static void show_version(shell_interaction & dialog, const char *command) { string cmd; line_tools_extract_basename(command, cmd); U_I maj, med, min; get_version(maj, med, min); dialog.change_non_interactive_output(cout); dialog.printf("\n %s version %s Copyright (C) 2002-2024 Denis Corbin\n\n", cmd.c_str(), DAR_SLAVE_VERSION); if(maj > 2) dialog.printf(gettext(" Using libdar %u.%u.%u built with compilation time options:\n"), maj, med, min); else dialog.printf(gettext(" Using libdar %u.%u built with compilation time options:\n"), maj, min); line_tools_display_features(dialog); dialog.printf("\n"); dialog.printf(gettext(" compiled the %s with %s version %s\n"), __DATE__, CC_NAT, __VERSION__); dialog.printf(gettext(" %s is part of the Disk ARchive suite (Release %s)\n"), cmd.c_str(), PACKAGE_VERSION); dialog.message(tools_printf(gettext(" %s comes with ABSOLUTELY NO WARRANTY;"), cmd.c_str()) + tools_printf(gettext(" for details\n type `dar -W'.")) + tools_printf(gettext(" This is free software, and you are welcome\n to redistribute it under certain conditions;")) + tools_printf(gettext(" type `dar -L | more'\n for details.\n\n"))); } dar-2.7.15/src/dar_suite/dar_suite.cpp0000644000175000017500000002232714636066467014556 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_LOCALE_H #include #endif #if HAVE_SIGNAL_H #include #endif #if HAVE_STDLIB_H #include #endif #if MUTEX_WORKS #if HAVE_PTHREAD_H #include #else #define pthread_t U_I #endif #endif } #include #include #include "dar_suite.hpp" #include "libdar.hpp" #include "line_tools.hpp" #include "shell_interaction.hpp" #if HAVE_LIBTHREADAR_LIBTHREADAR_HPP #include #endif using namespace libdar; using namespace std; static shared_ptr ui; static void signals_abort(int l, bool now); static void signal_abort_delayed(int l); static void signal_abort_now(int l); static void general_report(const std::string & msg); void dar_suite_reset_signal_handler() { #if HAVE_SIGNAL_H signal(SIGTERM, &signal_abort_delayed); signal(SIGINT, &signal_abort_delayed); signal(SIGQUIT, &signal_abort_delayed); signal(SIGHUP, &signal_abort_delayed); signal(SIGUSR1, &signal_abort_delayed); signal(SIGUSR2, &signal_abort_now); #if GPGME_SUPPORT // for GPGME: signal(SIGPIPE, SIG_IGN); #endif #endif } int dar_suite_global(int argc, char * const argv[], const char **env, const char *getopt_string, #if HAVE_GETOPT_LONG const struct option *long_options, #endif char stop_scan, cli_callback call) { int ret = EXIT_OK; dar_suite_reset_signal_handler(); #ifdef ENABLE_NLS // gettext settings try { if(string(DAR_LOCALEDIR) != string("")) if(bindtextdomain(PACKAGE, DAR_LOCALEDIR) == nullptr) throw Erange("", "Cannot open the translated messages directory, native language support will not work"); if(setlocale(LC_MESSAGES, "") == nullptr || setlocale(LC_CTYPE, "") == nullptr) throw Erange("", "Cannot set locale category, native language support will not work"); if(textdomain(PACKAGE) == nullptr) throw Erange("", "Cannot find dar's catalogue, native language support will not work"); } catch(Erange & e) { cerr << e.get_message() << endl; } #endif try { U_I min, med, maj; bool silent; line_tools_look_for_Q(argc, argv, getopt_string, #if HAVE_GETOPT_LONG long_options, #endif stop_scan, silent); ui.reset(new (nothrow) shell_interaction(cerr, cerr, silent)); if(!ui) throw Ememory("dar_suite_global"); try { get_version(maj, med, min); } catch(Erange & e) { if(e.get_source() == "libdar_init_gpgme") ui->pause("INITIALIZATION FAILED FOR GPGME, missing gpg binary? Retry initializing without gpgme support, which may lead libdar to silently fail reading or writing gpg ciphered archive)?"); close_and_clean(); get_version(maj, med, min, true, false); } if(maj != LIBDAR_COMPILE_TIME_MAJOR || med < LIBDAR_COMPILE_TIME_MEDIUM) { general_report(tools_printf(gettext("We have linked with an incompatible version of libdar. Expecting version %d.%d.x but having linked with version %d.%d.%d"), LIBDAR_COMPILE_TIME_MAJOR, LIBDAR_COMPILE_TIME_MEDIUM, maj, med, min)); ret = EXIT_ERROR; } else ret = (*call)(ui, argc, argv, env); // closing libdar close_and_clean(); } catch(Efeature & e) { general_report(string(gettext("NOT YET IMPLEMENTED FEATURE has been asked: ")) + e.get_message()); general_report(string(gettext("Please check documentation or upgrade your software if available"))); ret = EXIT_SYNTAX; } catch(Ehardware & e) { general_report(string(gettext("SEEMS TO BE A HARDWARE PROBLEM: "))+e.get_message()); general_report(string(gettext("Please check your hardware"))); ret = EXIT_ERROR; } catch(Esecu_memory & e) { general_report(string(gettext("Lack of SECURED memory to achieve the operation, aborting operation"))); ret = EXIT_ERROR; } catch(Ememory & e) { general_report(string(gettext("Lack of memory to achieve the operation, aborting operation"))); ret = EXIT_ERROR; } catch(std::bad_alloc & e) { general_report(string(gettext("Lack of memory to achieve the operation, aborting operation"))); ret = EXIT_ERROR; } catch(Erange & e) { general_report(string(gettext("FATAL error, aborting operation: ")) + e.get_message()); ret = EXIT_ERROR; } catch(Euser_abort & e) { general_report(string(gettext("Aborting program. User refused to continue while asking: ")) + e.get_message()); ret = EXIT_USER_ABORT; } catch(Ethread_cancel & e) { general_report(string(gettext("Program has been aborted for the following reason: ")) + e.get_message()); ret = EXIT_USER_ABORT; } catch(Edata & e) { general_report(e.get_message()); ret = EXIT_DATA_ERROR; } catch(Escript & e) { general_report(string(gettext("Aborting program. An error occurred concerning user command execution: ")) + e.get_message()); ret = EXIT_SCRIPT_ERROR; } catch(Elibcall & e) { general_report(string(gettext("Aborting program. An error occurred while calling libdar: ")) + e.get_message()); ret = EXIT_LIBDAR; } catch(Einfinint & e) { general_report(string(gettext("Aborting program. ")) + e.get_message()); ret = EXIT_BUG; } catch(Elimitint & e) { general_report(string(gettext("Aborting program. ")) + e.get_message()); ret = EXIT_LIMITINT; } catch(Ecompilation & e) { general_report(string(gettext("Aborting program. The requested operation needs a feature that has been disabled at compilation time: ")) + e.get_message()); ret = EXIT_COMPILATION; } catch(Esystem & e) { general_report(string(gettext("FATAL error, aborting operation: ")) + e.get_message()); ret = EXIT_ERROR; } catch(Enet_auth & e) { general_report(string(gettext("FATAL error during network communication, aborting operation: ")) + e.get_message()); ret = EXIT_ERROR; } catch(Egeneric & e) { cerr << e.dump_str(); general_report(string(gettext("INTERNAL ERROR, PLEASE REPORT THE PREVIOUS OUTPUT TO MAINTAINER"))); ret = EXIT_BUG; } #ifdef LIBTHREADAR_AVAILABLE catch(libthreadar::exception_base & e) { string msg = ""; for(unsigned int i = 0; i < e.size() ; ++i) msg += e[i]; cerr << msg << endl; ret = EXIT_BUG; } #endif catch(...) { Ebug x = SRC_BUG; cerr << x.dump_str(); general_report(string(gettext("CAUGHT A NON (LIB)DAR EXCEPTION"))); general_report(string(gettext("INTERNAL ERROR, PLEASE REPORT THE PREVIOUS OUTPUT TO MAINTAINER"))); ret = EXIT_BUG; } #if MUTEX_WORKS if(get_thread_count() != 0) { general_report(string(gettext("SANITY CHECK: AT LEAST ONE THREAD_CANCELLATION OBJECT HAS NOT BEEN DESTROYED AND REMAINS IN MEMORY WHILE THE PROGRAM REACHED ITS END"))); } #endif ui.reset(); return ret; } string dar_suite_command_line_features() { #if HAVE_GETOPT_LONG const char *long_opt = gettext("YES"); #else const char *long_opt = gettext("NO"); #endif return tools_printf(gettext("Long options support : %s\n"), long_opt); } static void signal_abort_delayed(int l) { signals_abort(l, false); } static void signal_abort_now(int l) { signals_abort(l, true); } static void signals_abort(int l, bool now) { #if HAVE_DECL_SYS_SIGLIST general_report(tools_printf(gettext("Received signal: %s"), sys_siglist[l])); #else general_report(tools_printf(gettext("Received signal: %d"), l)); #endif #if MUTEX_WORKS if(now) { general_report(string(gettext("Archive fast termination engaged"))); } else { general_report(string(gettext("Archive delayed termination engaged"))); } #if HAVE_SIGNAL_H signal(l, SIG_DFL); general_report(string(gettext("Disabling signal handler, the next time this signal is received the program will abort immediately"))); #endif cancel_thread(pthread_self(), now); #else general_report(string(gettext("Cannot cleanly abort the operation, thread-safe support is missing, will thus abruptly stop the program, generated archive may be unusable"))); exit(EXIT_USER_ABORT); #endif } static void general_report(const std::string & msg) { if(ui) { shell_interaction *ptr = dynamic_cast(ui.get()); if(ptr != nullptr) ptr->change_non_interactive_output(cerr); ui->message(msg); } else cerr << msg << endl; } dar-2.7.15/src/dar_suite/no_comment.hpp0000644000175000017500000000313314636066467014734 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file no_comment.hpp /// \brief contains class that transparently strips out the comments from configuration file /// \ingroup CMDLINE #ifndef NO_COMMENT_HPP #define NO_COMMENT_HPP #include "../my_config.h" #include "hide_file.hpp" /// \addtogroup CMDLINE /// @{ class no_comment : public hide_file { public: no_comment(libdar::generic_file & f) : hide_file(f) {}; no_comment(const no_comment & ref) = default; no_comment & operator = (const no_comment & ref) = default; ~no_comment() = default; protected: virtual void fill_morceau() override; }; /// @} #endif dar-2.7.15/src/dar_suite/Makefile.in0000644000175000017500000010635614640025155014125 00000000000000# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### # # conditionnal part: --enable-build-static # VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = dar$(EXEEXT) dar_xform$(EXEEXT) dar_slave$(EXEEXT) \ dar_manager$(EXEEXT) dar_cp$(EXEEXT) dar_split$(EXEEXT) \ $(am__EXEEXT_1) subdir = src/dar_suite ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @BUILD_DAR_STATIC_TRUE@am__EXEEXT_1 = dar_static$(EXEEXT) \ @BUILD_DAR_STATIC_TRUE@ dar_cp_static$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) am_dar_OBJECTS = command_line.$(OBJEXT) config_file.$(OBJEXT) \ dar.$(OBJEXT) dar_suite.$(OBJEXT) hide_file.$(OBJEXT) \ no_comment.$(OBJEXT) line_tools.$(OBJEXT) \ crit_action_cmd_line.$(OBJEXT) dar_OBJECTS = $(am_dar_OBJECTS) dar_LDADD = $(LDADD) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_dar_cp_OBJECTS = dar_suite.$(OBJEXT) dar_cp.$(OBJEXT) \ line_tools.$(OBJEXT) dar_cp_OBJECTS = $(am_dar_cp_OBJECTS) dar_cp_LDADD = $(LDADD) am_dar_cp_static_OBJECTS = dar_suite.$(OBJEXT) dar_cp.$(OBJEXT) \ line_tools.$(OBJEXT) dar_cp_static_OBJECTS = $(am_dar_cp_static_OBJECTS) dar_cp_static_LDADD = $(LDADD) dar_cp_static_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(dar_cp_static_LDFLAGS) $(LDFLAGS) \ -o $@ am_dar_manager_OBJECTS = dar_suite.$(OBJEXT) dar_manager.$(OBJEXT) \ line_tools.$(OBJEXT) no_comment.$(OBJEXT) hide_file.$(OBJEXT) dar_manager_OBJECTS = $(am_dar_manager_OBJECTS) dar_manager_LDADD = $(LDADD) am_dar_slave_OBJECTS = dar_suite.$(OBJEXT) dar_slave.$(OBJEXT) \ line_tools.$(OBJEXT) dar_slave_OBJECTS = $(am_dar_slave_OBJECTS) dar_slave_LDADD = $(LDADD) am_dar_split_OBJECTS = dar_split-dar_split.$(OBJEXT) dar_split_OBJECTS = $(am_dar_split_OBJECTS) dar_split_LDADD = $(LDADD) dar_split_DEPENDENCIES = ../libdar/$(MYLIB).la $(am__DEPENDENCIES_1) am_dar_static_OBJECTS = command_line.$(OBJEXT) config_file.$(OBJEXT) \ dar.$(OBJEXT) dar_suite.$(OBJEXT) hide_file.$(OBJEXT) \ no_comment.$(OBJEXT) line_tools.$(OBJEXT) \ crit_action_cmd_line.$(OBJEXT) dar_static_OBJECTS = $(am_dar_static_OBJECTS) dar_static_LDADD = $(LDADD) dar_static_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(dar_static_LDFLAGS) $(LDFLAGS) -o $@ am_dar_xform_OBJECTS = dar_suite.$(OBJEXT) dar_xform.$(OBJEXT) \ line_tools.$(OBJEXT) dar_xform_OBJECTS = $(am_dar_xform_OBJECTS) dar_xform_LDADD = $(LDADD) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/command_line.Po \ ./$(DEPDIR)/config_file.Po ./$(DEPDIR)/crit_action_cmd_line.Po \ ./$(DEPDIR)/dar.Po ./$(DEPDIR)/dar_cp.Po \ ./$(DEPDIR)/dar_manager.Po ./$(DEPDIR)/dar_slave.Po \ ./$(DEPDIR)/dar_split-dar_split.Po ./$(DEPDIR)/dar_suite.Po \ ./$(DEPDIR)/dar_xform.Po ./$(DEPDIR)/hide_file.Po \ ./$(DEPDIR)/line_tools.Po ./$(DEPDIR)/no_comment.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(dar_SOURCES) $(dar_cp_SOURCES) $(dar_cp_static_SOURCES) \ $(dar_manager_SOURCES) $(dar_slave_SOURCES) \ $(dar_split_SOURCES) $(dar_static_SOURCES) \ $(dar_xform_SOURCES) DIST_SOURCES = $(dar_SOURCES) $(dar_cp_SOURCES) \ $(dar_cp_static_SOURCES) $(dar_manager_SOURCES) \ $(dar_slave_SOURCES) $(dar_split_SOURCES) \ $(dar_static_SOURCES) $(dar_xform_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CXXSTDFLAGS = @CXXSTDFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN_PROG = @DOXYGEN_PROG@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GPGME_CFLAGS = @GPGME_CFLAGS@ GPGME_CONFIG = @GPGME_CONFIG@ GPGME_LIBS = @GPGME_LIBS@ GPGRT_CONFIG = @GPGRT_CONFIG@ GREP = @GREP@ HAS_DOT = @HAS_DOT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBCURL_CFLAGS = @LIBCURL_CFLAGS@ LIBCURL_LIBS = @LIBCURL_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTHREADAR_CFLAGS = @LIBTHREADAR_CFLAGS@ LIBTHREADAR_LIBS = @LIBTHREADAR_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGMERGE = @MSGMERGE@ MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POSUB = @POSUB@ PYEXT = @PYEXT@ PYFLAGS = @PYFLAGS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ UPX_PROG = @UPX_PROG@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dot = @dot@ doxygen = @doxygen@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ groff = @groff@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ tmp = @tmp@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ upx = @upx@ @BUILD_DAR_STATIC_FALSE@ALSO_BUILD = @BUILD_DAR_STATIC_TRUE@ALSO_BUILD = dar_static dar_cp_static @PROFILING_FALSE@LD_PROF = # # Conditionnal part : Profiling # @PROFILING_TRUE@LD_PROF = -pg @PROFILING_FALSE@CPP_PROF = @PROFILING_TRUE@CPP_PROF = -pg @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@MYLIB = libdar @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@MYLIB = libdar64 # # conditionnal part: --enable-mode=... # @BUILD_MODE32_TRUE@MYLIB = libdar32 @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@AM_CPPFLAGS = -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(sysconfdir)\" $(CPP_PROF) @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@AM_CPPFLAGS = -DLIBDAR_MODE=64 -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(sysconfdir)\" $(CPP_PROF) @BUILD_MODE32_TRUE@AM_CPPFLAGS = -DLIBDAR_MODE=32 -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(sysconfdir)\" $(CPP_PROF) @BUILD_DAR_STATIC_FALSE@@USE_UPX_TRUE@ALSO_BUILD_INST = # # conditionnal part UPX # @BUILD_DAR_STATIC_TRUE@@USE_UPX_TRUE@ALSO_BUILD_INST = $(ALSO_BUILD)$(EXEEXT) LDADD = ../libdar/$(MYLIB).la $(LTLIBINTL) @DEBUG_STATIC_FALSE@AM_LDFLAGS = $(LD_PROF) @DEBUG_STATIC_TRUE@AM_LDFLAGS = -all-static $(LD_PROF) dar_SOURCES = command_line.cpp command_line.hpp config_file.cpp config_file.hpp dar.cpp dar.hpp dar_suite.cpp dar_suite.hpp hide_file.cpp hide_file.hpp no_comment.cpp no_comment.hpp getopt_decision.h my_getopt_long.h line_tools.hpp line_tools.cpp crit_action_cmd_line.cpp crit_action_cmd_line.hpp dar_DEPENDENCIES = ../libdar/$(MYLIB).la dar_xform_SOURCES = dar_suite.cpp dar_suite.hpp dar_xform.cpp line_tools.hpp line_tools.cpp dar_xform_DEPENDENCIES = ../libdar/$(MYLIB).la dar_slave_SOURCES = dar_suite.cpp dar_suite.hpp dar_slave.cpp line_tools.hpp line_tools.cpp dar_slave_DEPENDENCIES = ../libdar/$(MYLIB).la dar_manager_SOURCES = dar_suite.cpp dar_suite.hpp dar_manager.cpp getopt_decision.h my_getopt_long.h line_tools.hpp line_tools.cpp no_comment.cpp no_comment.hpp hide_file.cpp hide_file.hpp dar_manager_DEPENDENCIES = ../libdar/$(MYLIB).la dar_cp_SOURCES = dar_suite.cpp dar_suite.hpp dar_cp.cpp line_tools.hpp line_tools.cpp dar_cp_DEPENDENCIES = ../libdar/$(MYLIB).la dar_split_SOURCES = dar_split.c dar_split_CPPFLAGS = -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(sysconfdir)\" $(CPP_PROF) # # dar_static ( --enable-dar-static ) # dar_static_SOURCES = command_line.cpp command_line.hpp config_file.cpp config_file.hpp dar.cpp dar.hpp dar_suite.cpp dar_suite.hpp hide_file.cpp hide_file.hpp no_comment.cpp no_comment.hpp getopt_decision.h my_getopt_long.h line_tools.hpp line_tools.cpp crit_action_cmd_line.cpp crit_action_cmd_line.hpp dar_static_DEPENDENCIES = ../libdar/$(MYLIB).la dar_static_LDFLAGS = -all-static $(AM_LDFLAGS) dar_cp_static_SOURCES = dar_suite.cpp dar_suite.hpp dar_cp.cpp line_tools.hpp line_tools.cpp dar_cp_static_DEPENDENCIES = ../libdar/$(MYLIB).la dar_cp_static_LDFLAGS = -all-static $(AM_LDFLAGS) all: all-am .SUFFIXES: .SUFFIXES: .c .cpp .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/dar_suite/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/dar_suite/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list dar$(EXEEXT): $(dar_OBJECTS) $(dar_DEPENDENCIES) $(EXTRA_dar_DEPENDENCIES) @rm -f dar$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(dar_OBJECTS) $(dar_LDADD) $(LIBS) dar_cp$(EXEEXT): $(dar_cp_OBJECTS) $(dar_cp_DEPENDENCIES) $(EXTRA_dar_cp_DEPENDENCIES) @rm -f dar_cp$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(dar_cp_OBJECTS) $(dar_cp_LDADD) $(LIBS) dar_cp_static$(EXEEXT): $(dar_cp_static_OBJECTS) $(dar_cp_static_DEPENDENCIES) $(EXTRA_dar_cp_static_DEPENDENCIES) @rm -f dar_cp_static$(EXEEXT) $(AM_V_CXXLD)$(dar_cp_static_LINK) $(dar_cp_static_OBJECTS) $(dar_cp_static_LDADD) $(LIBS) dar_manager$(EXEEXT): $(dar_manager_OBJECTS) $(dar_manager_DEPENDENCIES) $(EXTRA_dar_manager_DEPENDENCIES) @rm -f dar_manager$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(dar_manager_OBJECTS) $(dar_manager_LDADD) $(LIBS) dar_slave$(EXEEXT): $(dar_slave_OBJECTS) $(dar_slave_DEPENDENCIES) $(EXTRA_dar_slave_DEPENDENCIES) @rm -f dar_slave$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(dar_slave_OBJECTS) $(dar_slave_LDADD) $(LIBS) dar_split$(EXEEXT): $(dar_split_OBJECTS) $(dar_split_DEPENDENCIES) $(EXTRA_dar_split_DEPENDENCIES) @rm -f dar_split$(EXEEXT) $(AM_V_CCLD)$(LINK) $(dar_split_OBJECTS) $(dar_split_LDADD) $(LIBS) dar_static$(EXEEXT): $(dar_static_OBJECTS) $(dar_static_DEPENDENCIES) $(EXTRA_dar_static_DEPENDENCIES) @rm -f dar_static$(EXEEXT) $(AM_V_CXXLD)$(dar_static_LINK) $(dar_static_OBJECTS) $(dar_static_LDADD) $(LIBS) dar_xform$(EXEEXT): $(dar_xform_OBJECTS) $(dar_xform_DEPENDENCIES) $(EXTRA_dar_xform_DEPENDENCIES) @rm -f dar_xform$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(dar_xform_OBJECTS) $(dar_xform_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command_line.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_file.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crit_action_cmd_line.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dar.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dar_cp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dar_manager.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dar_slave.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dar_split-dar_split.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dar_suite.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dar_xform.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hide_file.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/line_tools.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/no_comment.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< dar_split-dar_split.o: dar_split.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dar_split_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dar_split-dar_split.o -MD -MP -MF $(DEPDIR)/dar_split-dar_split.Tpo -c -o dar_split-dar_split.o `test -f 'dar_split.c' || echo '$(srcdir)/'`dar_split.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dar_split-dar_split.Tpo $(DEPDIR)/dar_split-dar_split.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dar_split.c' object='dar_split-dar_split.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dar_split_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dar_split-dar_split.o `test -f 'dar_split.c' || echo '$(srcdir)/'`dar_split.c dar_split-dar_split.obj: dar_split.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dar_split_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dar_split-dar_split.obj -MD -MP -MF $(DEPDIR)/dar_split-dar_split.Tpo -c -o dar_split-dar_split.obj `if test -f 'dar_split.c'; then $(CYGPATH_W) 'dar_split.c'; else $(CYGPATH_W) '$(srcdir)/dar_split.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dar_split-dar_split.Tpo $(DEPDIR)/dar_split-dar_split.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dar_split.c' object='dar_split-dar_split.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dar_split_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dar_split-dar_split.obj `if test -f 'dar_split.c'; then $(CYGPATH_W) 'dar_split.c'; else $(CYGPATH_W) '$(srcdir)/dar_split.c'; fi` .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libtool clean-local \ mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/command_line.Po -rm -f ./$(DEPDIR)/config_file.Po -rm -f ./$(DEPDIR)/crit_action_cmd_line.Po -rm -f ./$(DEPDIR)/dar.Po -rm -f ./$(DEPDIR)/dar_cp.Po -rm -f ./$(DEPDIR)/dar_manager.Po -rm -f ./$(DEPDIR)/dar_slave.Po -rm -f ./$(DEPDIR)/dar_split-dar_split.Po -rm -f ./$(DEPDIR)/dar_suite.Po -rm -f ./$(DEPDIR)/dar_xform.Po -rm -f ./$(DEPDIR)/hide_file.Po -rm -f ./$(DEPDIR)/line_tools.Po -rm -f ./$(DEPDIR)/no_comment.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/command_line.Po -rm -f ./$(DEPDIR)/config_file.Po -rm -f ./$(DEPDIR)/crit_action_cmd_line.Po -rm -f ./$(DEPDIR)/dar.Po -rm -f ./$(DEPDIR)/dar_cp.Po -rm -f ./$(DEPDIR)/dar_manager.Po -rm -f ./$(DEPDIR)/dar_slave.Po -rm -f ./$(DEPDIR)/dar_split-dar_split.Po -rm -f ./$(DEPDIR)/dar_suite.Po -rm -f ./$(DEPDIR)/dar_xform.Po -rm -f ./$(DEPDIR)/hide_file.Po -rm -f ./$(DEPDIR)/line_tools.Po -rm -f ./$(DEPDIR)/no_comment.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: install-am install-exec-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-binPROGRAMS clean-generic clean-libtool clean-local \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-binPROGRAMS install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-exec-hook \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-binPROGRAMS .PRECIOUS: Makefile @PROFILING_TRUE@clean-local: @PROFILING_TRUE@ rm -f gmon.out @PROFILING_FALSE@clean-local: @USE_UPX_TRUE@install-exec-hook : @USE_UPX_TRUE@ cd $(DESTDIR)$(bindir) ; $(UPX_PROG) -9 dar$(EXEEXT) dar_xform$(EXEEXT) dar_slave$(EXEEXT) dar_manager$(EXEEXT) dar_cp$(EXEEXT) dar_split$(EXEEXT) $(ALSO_BUILD_INST) || exit 0 # exit 0 because dar_split for example is today too small to be upx-compressed @USE_UPX_FALSE@install-exec-hook: @USE_UPX_FALSE@ echo "no executable packing, UPX not found or disabled" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dar-2.7.15/src/dar_suite/hide_file.hpp0000644000175000017500000000563114636066467014513 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file hide_file.hpp /// \brief contains class of base to split files in words /// \ingroup CMDLINE #ifndef HIDE_FILE_HPP #define HIDE_FILE_HPP #include "../my_config.h" #include #include "libdar.hpp" #include "generic_file.hpp" using namespace libdar; using namespace std; /// \addtogroup CMDLINE /// @{ class hide_file : public generic_file { public: hide_file(generic_file &f); hide_file(const hide_file & ref) = default; hide_file & operator = (const hide_file & ref) = default; ~hide_file() = default; virtual bool skippable(skippability direction, const infinint & amount) override { return true; }; virtual bool skip(const infinint & pos) override; virtual bool skip_to_eof() override; virtual bool skip_relative(S_I x) override; virtual bool truncatable(const infinint & pos) const override { return false; }; virtual infinint get_position() const override; protected: struct partie { infinint debut, longueur; // debut is the offset in ref file infinint offset; // offset in the resulting file }; vector morceau; generic_file *ref; virtual void inherited_read_ahead(const infinint & amount) override { ref->read_ahead(amount); }; virtual U_I inherited_read(char *a, U_I size) override; virtual void inherited_write(const char *a, size_t size) override; virtual void inherited_truncate(const infinint & pos) override { throw Efeature("truncate a hide file"); }; virtual void inherited_sync_write() override {}; virtual void inherited_flush_read() override {}; virtual void inherited_terminate() override {}; virtual void fill_morceau() = 0; // the inherited classes have with this method // to fill the "morceau" variable that defines // the portions private: U_I pos_index; infinint pos_relicat; bool is_init; void init() const; }; /// @} #endif dar-2.7.15/src/dar_suite/Makefile.am0000644000175000017500000001042214636066467014120 00000000000000####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### # # conditionnal part: --enable-build-static # if BUILD_DAR_STATIC ALSO_BUILD = dar_static dar_cp_static else ALSO_BUILD = endif # # Conditionnal part : Profiling # if PROFILING LD_PROF = -pg CPP_PROF = -pg clean-local: rm -f gmon.out else LD_PROF = CPP_PROF = clean-local: endif # # conditionnal part: --enable-mode=... # if BUILD_MODE32 MYLIB=libdar32 AM_CPPFLAGS=-DLIBDAR_MODE=32 -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(sysconfdir)\" $(CPP_PROF) else if BUILD_MODE64 MYLIB=libdar64 AM_CPPFLAGS=-DLIBDAR_MODE=64 -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(sysconfdir)\" $(CPP_PROF) else MYLIB=libdar AM_CPPFLAGS=-I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(sysconfdir)\" $(CPP_PROF) endif endif # # conditionnal part UPX # if USE_UPX if BUILD_DAR_STATIC ALSO_BUILD_INST = $(ALSO_BUILD)$(EXEEXT) else ALSO_BUILD_INST = endif install-exec-hook : cd $(DESTDIR)$(bindir) ; $(UPX_PROG) -9 dar$(EXEEXT) dar_xform$(EXEEXT) dar_slave$(EXEEXT) dar_manager$(EXEEXT) dar_cp$(EXEEXT) dar_split$(EXEEXT) $(ALSO_BUILD_INST) || exit 0 # exit 0 because dar_split for example is today too small to be upx-compressed else install-exec-hook: echo "no executable packing, UPX not found or disabled" endif # # unconditionnal part # bin_PROGRAMS = dar dar_xform dar_slave dar_manager dar_cp dar_split $(ALSO_BUILD) LDADD = ../libdar/$(MYLIB).la $(LTLIBINTL) if DEBUG_STATIC AM_LDFLAGS = -all-static $(LD_PROF) else AM_LDFLAGS = $(LD_PROF) endif dar_SOURCES = command_line.cpp command_line.hpp config_file.cpp config_file.hpp dar.cpp dar.hpp dar_suite.cpp dar_suite.hpp hide_file.cpp hide_file.hpp no_comment.cpp no_comment.hpp getopt_decision.h my_getopt_long.h line_tools.hpp line_tools.cpp crit_action_cmd_line.cpp crit_action_cmd_line.hpp dar_DEPENDENCIES = ../libdar/$(MYLIB).la dar_xform_SOURCES = dar_suite.cpp dar_suite.hpp dar_xform.cpp line_tools.hpp line_tools.cpp dar_xform_DEPENDENCIES = ../libdar/$(MYLIB).la dar_slave_SOURCES = dar_suite.cpp dar_suite.hpp dar_slave.cpp line_tools.hpp line_tools.cpp dar_slave_DEPENDENCIES = ../libdar/$(MYLIB).la dar_manager_SOURCES = dar_suite.cpp dar_suite.hpp dar_manager.cpp getopt_decision.h my_getopt_long.h line_tools.hpp line_tools.cpp no_comment.cpp no_comment.hpp hide_file.cpp hide_file.hpp dar_manager_DEPENDENCIES = ../libdar/$(MYLIB).la dar_cp_SOURCES = dar_suite.cpp dar_suite.hpp dar_cp.cpp line_tools.hpp line_tools.cpp dar_cp_DEPENDENCIES = ../libdar/$(MYLIB).la dar_split_SOURCES = dar_split.c dar_split_CPPFLAGS = -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(sysconfdir)\" $(CPP_PROF) # # dar_static ( --enable-dar-static ) # dar_static_SOURCES = command_line.cpp command_line.hpp config_file.cpp config_file.hpp dar.cpp dar.hpp dar_suite.cpp dar_suite.hpp hide_file.cpp hide_file.hpp no_comment.cpp no_comment.hpp getopt_decision.h my_getopt_long.h line_tools.hpp line_tools.cpp crit_action_cmd_line.cpp crit_action_cmd_line.hpp dar_static_DEPENDENCIES = ../libdar/$(MYLIB).la dar_static_LDFLAGS = -all-static $(AM_LDFLAGS) dar_cp_static_SOURCES = dar_suite.cpp dar_suite.hpp dar_cp.cpp line_tools.hpp line_tools.cpp dar_cp_static_DEPENDENCIES = ../libdar/$(MYLIB).la dar_cp_static_LDFLAGS = -all-static $(AM_LDFLAGS) dar-2.7.15/src/dar_suite/dar.hpp0000644000175000017500000000300114636066467013336 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file dar.hpp /// \brief main module for dar command-line tool /// \ingroup CMDLINE /// \defgroup CMDLINE CMDLINE /// \brief command-line interface /// /// this module contains all the command-line specific routines, classes and datastructures. The intention of /// documenting that part is for illustration and example of libdar usage. #ifndef DAR_HPP #define DAR_HPP #include "../my_config.h" /// \addtogroup CMDLINE /// @{ extern const char *dar_version(); /// @} #endif dar-2.7.15/src/dar_suite/dar_suite.hpp0000644000175000017500000001010414636066467014551 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file dar_suite.hpp /// \brief contains routine to manage CLI's common initialization and ultimate exception catching /// \ingroup CMDLINE #ifndef DAR_SUITE_HPP #define DAR_SUITE_HPP #include "../my_config.h" #include "libdar.hpp" #include #define EXIT_OK 0 // all that was asked is done #define EXIT_SYNTAX 1 // syntax error on command line #define EXIT_ERROR 2 // error not related to the data treated // (lack of memory, hardware problem, etc.) #define EXIT_BUG 3 // detected a condition that should never happen #define EXIT_USER_ABORT 4 // user asked to abort (or question in non // interactive mode) #define EXIT_DATA_ERROR 5 // error in data treated (could not save/restore/ // compare all data due for example to bad access permission. Comparison // mismatch of some files, archive testing failed etc...) #define EXIT_SCRIPT_ERROR 6 // error around the execution of a user command // using -E or -F options #define EXIT_LIBDAR 7 // error calling libdar. Arguments given to libdar // do not match those expected (sanity checks warning). #define EXIT_LIMITINT 8 // limitinit overflow // fixed using full infinint version of the program #define EXIT_UNKNOWN_ERROR 9 // error not possible to report by other mean no access to stdout/stderr) #define EXIT_COMPILATION 10 // feature not activated at compilation time #define EXIT_SAVED_MODIFIED 11 // some files have been modified at the time they were saved #define EXTENSION "dar" /// the compiler version MACRO #ifndef __VERSION__ #define __VERSION__ "unknown" #endif /// the compiler Nature MACRO #ifdef __GNUC__ #define CC_NAT "GNUC" #else #define CC_NAT "unknown" #endif using namespace libdar; /// \addtogroup CMDLINE /// @{ using cli_callback = S_I (*)(std::shared_ptr & dialog, int, char *const [], const char **env); extern void dar_suite_reset_signal_handler(); /// common routine for all dar command-line tools to initialize environment and convert uncaught exceptions to exit status code /// \param[in] argc is the number of argument on the command line /// \param[in] argv is the list of arguments on the command line /// \param[in] env is the environment variables table obtained from main() /// \param[in] getopt_string is the parsing string to pass to getopt #if HAVE_GETOPT_LONG /// \param[in] long_options is the optional list of long options (an nullptr pointer is acceptable for no long option) #endif /// \param[in] stop_scan while looking early for -j and -Q option will ignore all that follows stop_scan option if met /// \param[in] call is a callback function to run once user interaction is initialized and to catch from the exceptions /// \return the application exist status to use extern int dar_suite_global(int argc, char * const argv[], const char **env, const char *getopt_string, #if HAVE_GETOPT_LONG const struct option *long_options, #endif char stop_scan, cli_callback call); extern std::string dar_suite_command_line_features(); /// @} #endif dar-2.7.15/src/dar_suite/dar_split.c0000644000175000017500000003525314636066467014222 00000000000000/********************************************************************* // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file ******************************************************************** */ #include "../my_config.h" #include "cygwin_adapt.h" #if HAVE_STDIO_H #include #endif #if HAVE_SIGNAL_H #include #endif #if HAVE_TERMIOS_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_SYS_TYPE_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_STDLIB_H #include #endif #if HAVE_LIMITS_H #include #endif #if HAVE_GETOPT_H #include #endif #if HAVE_SYS_TIME_H #include #endif /// the compiler Nature MACRO #ifdef __GNUC__ #define CC_NAT "GNUC" #else #define CC_NAT "unknown" #endif #define BUFSIZE 102400 #ifdef SSIZE_MAX #if SSIZE_MAX < BUFSIZE #undef BUFSIZE #define BUFSIZE SSIZE_MAX #endif #endif #define KEY_INPUT "split_input" #define KEY_OUTPUT "split_output" #define DAR_SPLIT_VERSION "1.2.2" #define ERR_SYNTAX 1 #define ERR_TIMER 2 #define ERR_BUG 3 static void usage(char *a); static void show_version(char *a); static int init(); static void blocking_read(int fd, int mode); static void purge_fd(int fd); static void stop_and_wait(); static void pipe_handle_pause(int x); static void pipe_handle_end(int x); static void alarm_handle(int x); static void normal_read_to_multiple_write(char *filename, int sync_mode, unsigned int bs, unsigned int rate); static void multi_read_to_normal_write(char *filename, unsigned int bs, unsigned int rate, unsigned int count); static int open_read(char *filemane); /* returns the filedescriptor */ static int open_write(char *filename, int sync_mode); /* returns the filedescriptor */ static void init_timer(unsigned int bufsize, unsigned int rate); static void rate_limit(unsigned int rate); static int fd_inter = -1; static int can_go = 0; int main(int argc, char *argv[]) { signed int lu; /* variables used to carry arguments */ int split_mode = 0; /* 0 : unset, 1 = normal_read , 2 = normal_write */ char *splitted_file = NULL; int sync_mode = 0; unsigned int bs = 0; unsigned int rate = 0; unsigned int count = 0; /* command line parsing */ while((lu = getopt(argc, argv, "-sb:hvVr:c:")) != EOF) { switch(lu) { case 1: /* non option argument due to the initial '-' in getopt string */ if(split_mode == 0) { if(strcmp(KEY_OUTPUT, optarg) == 0) split_mode = 1; else if(strcmp(KEY_INPUT, optarg) == 0) split_mode = 2; else { fprintf(stderr, "Unknown mode %s. Please use either %s or %s\n", optarg, KEY_OUTPUT, KEY_INPUT); return ERR_SYNTAX; } } else /* now reading the filename */ splitted_file = optarg; break; case 's': sync_mode = 1; break; case 'b': bs = atoi(optarg); break; case 'r': rate = atoi(optarg); break; case 'c': count = atoi(optarg); break; case 'h': usage(argv[0]); return 0; case 'v': case 'V': show_version(argv[0]); return 0; case ':': fprintf(stderr, "Missing parameter to option -%c\n", (char)optopt); return ERR_SYNTAX; case '?': fprintf(stderr, "Ignoring unknown option -%c\n", (char)optopt); return ERR_SYNTAX; } } if(splitted_file == NULL) { fprintf(stderr, "Missing filename argument\n"); return ERR_SYNTAX; } if(count > 0 && split_mode != 2) { fprintf(stderr, "-c option is only available in split_input mode\n"); return ERR_SYNTAX; } if(sync_mode == 1 && split_mode != 1) { fprintf(stderr, "-s option is only available in split_output mode\n"); return ERR_SYNTAX; } /* initialization */ if(!init()) return 2; /* execution */ switch(split_mode) { case 1: normal_read_to_multiple_write(splitted_file, sync_mode, bs, rate); break; case 2: multi_read_to_normal_write(splitted_file, bs, rate, count); break; default: fprintf(stderr, "missing read mode argument. Please user either %s or %s\n", KEY_OUTPUT, KEY_INPUT); return ERR_SYNTAX; } /* normal exit */ return 0; } static void usage(char *a) { fprintf(stderr, "\nusage: %s [-b ] [-r ] { [-c ] %s | [-s] %s } \n\n", a, KEY_INPUT, KEY_OUTPUT); fprintf(stderr, "- in %s mode, the data sent to %s's input is copied to the given filename\n which may possibly be a non permanent output (retrying to write in case of failure)\n", KEY_OUTPUT, a); fprintf(stderr, "- in %s mode, the data is read from the given filename which may possibly\n be non permanent input (retrying to read in case of failure) and copied to\n %s's output\n", KEY_INPUT, a); fprintf(stderr, "\nThe -s option for %s mode leads %s to issue SYNC writes, this avoids\n the operating system's caching to wrongly report a write as successful.\n This flag reduces write performances but may be necessary when the end of tape is not\n properly detected by %s\n", KEY_OUTPUT, a, a); fprintf(stderr, "With -b option the amount of bytes sent per read or write system call does\n not exceed this amount in byte\n"); fprintf(stderr, "With -r option the transfer is limited to the given byte/second rate\n"); fprintf(stderr, "With -c option dar_split will assume there is at most tapes and not ask\n for further ones\n"); } static void show_version(char *a) { fprintf(stderr, "\n %s version %s, Copyright (C) 2015 Denis Corbin\n", a, DAR_SPLIT_VERSION); fprintf(stderr, " compiled the %s with %s version %s\n", __DATE__, CC_NAT, __VERSION__); fprintf(stderr, " %s is part of the Disk ARchive suite (Release %s)\n", a, PACKAGE_VERSION); fprintf(stderr, " %s comes with ABSOLUTELY NO WARRANTY; for details\n type `dar -W'.", a); fprintf(stderr, " This is free software, and you are welcome\n to redistribute it under certain conditions;"); fprintf(stderr, " type `dar -L | more'\n for details.\n\n"); } static int init() { char tty[L_ctermid+1]; (void)ctermid(tty); tty[L_ctermid] = '\0'; fd_inter = open(tty, O_RDONLY); if(fd_inter < 0) { fprintf(stderr, "Cannot open filedescriptor to provide user interaction: %s\n", strerror(errno)); return 0; /* false */ } else return 1; /* true */ } static void blocking_read(int fd, int mode) { int flags = fcntl(fd_inter, F_GETFL, 0); /* code fetched from tools_blocking_read() but not usable as is because dar_split is written in basic C (not C++) */ if(flags < 0) fprintf(stderr, "Cannot read \"fcbtl\" file's flags: %s\n", strerror(errno)); else { if(!mode) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if(fcntl(fd, F_SETFL, flags) < 0) fprintf(stderr, "Cannot modify the NONBLOCK fcntl flag: %s", strerror(errno)); } } static void purge_fd(int fd) { static const int bufsize = 10; char buf[bufsize]; blocking_read(fd, 0 == 1); while(read(fd, buf, bufsize) >= 0) ; blocking_read(fd, 0 == 0); } static void stop_and_wait() { char tmp[10]; purge_fd(fd_inter); fprintf(stderr, "Press return when ready to continue or hit CTRL-C to abort\n"); (void)read(fd_inter, tmp, 3); } static void pipe_handle_pause(int x) { fprintf(stderr, "No reader to pipe the output data to, do something!\n"); stop_and_wait(); } static void pipe_handle_end(int x) { exit(0); } static void alarm_handle(int x) { can_go = 1; } static void normal_read_to_multiple_write(char *filename, int sync_mode, unsigned int bs, unsigned int rate) { unsigned int bufsize = (bs == 0 ? BUFSIZE : bs); char* buffer = (char*) malloc(bufsize); int lu; int ecru; int offset; int fd = buffer == NULL ? -1 : open_write(filename, sync_mode); int run = 1; size_t tape_size = 0; size_t step; if(buffer == NULL) { fprintf(stderr, "Error allocating %u bytes memory block: %s\n", BUFSIZE, strerror(errno)); run = 0; } signal(SIGPIPE, pipe_handle_pause); /* handling case when writing to pipe that has no reader */ init_timer(bufsize, rate); while(run) { rate_limit(rate); lu = read(0, buffer, bufsize); if(lu == 0) /* reached EOF */ break; /* exiting the while loop, end of processing */ if(lu < 0) { if(errno == EAGAIN || errno == EINTR) continue; /* start over the while loop */ else { fprintf(stderr, "Error reading data: %s\n", strerror(errno)); break; /* exiting the while loop, end of processing */ } } /* now we have data to write down */ offset = 0; step = lu; do { ecru = write(fd, buffer + offset, step); if(ecru < 0) /* an error occured */ { switch(errno) { case EAGAIN: case EINTR: break; case ENOSPC: if(step > 1) step /= 2; else { #if HAVE_SYNCFS syncfs(fd); #else sync(); #endif close(fd); fprintf(stderr, "No space left on destination after having written %lu bytes, please do something!\n", (unsigned long)tape_size); tape_size = 0; step = lu; stop_and_wait(); fd = open_write(filename, sync_mode); } break; default: fprintf(stderr, "Error writing data: %s\n", strerror(errno)); stop_and_wait(); } } else { if(ecru > lu) { fprintf(stderr, "BUG MET at line %d\n", __LINE__); exit(ERR_BUG); /* end or processing */ } else { /* starting from here no error occurred and ecru >= 0 */ offset += ecru; lu -= ecru; if(step > lu) step = lu; tape_size += ecru; } } } while(lu > 0); if(lu > 0) break; /* not all data could be written, aborting */ } fprintf(stderr, "%ld bytes written since the last media change", (unsigned long)tape_size); if(fd >= 0) { #if HAVE_SYNCFS syncfs(fd); #else sync(); #endif close(fd); } if(buffer != NULL) free(buffer); } static void multi_read_to_normal_write(char *filename, unsigned int bs, unsigned int rate, unsigned int count) { unsigned int bufsize = (bs == 0 ? BUFSIZE: bs); char* buffer = (char*)malloc(BUFSIZE); int lu; int ecru; int offset; int fd = buffer == NULL ? -1 : open_read(filename); int run = 1; int iter = 0; if(buffer == NULL) { fprintf(stderr, "Error allocating %u bytes memory block: %s\n", BUFSIZE, strerror(errno)); run = 0; } signal(SIGPIPE, pipe_handle_end); /* handling case when writing to pipe that has no reader */ init_timer(bufsize, rate); while(run) { rate_limit(rate); lu = read(fd, buffer, bufsize); if(lu == 0) // EOF { close(fd); ++iter; if(count == 0 || iter < count) { if(count == 0) fprintf(stderr, "No more data available from source, please do something!\n"); else fprintf(stderr, "No more data available from source number %d, please do something!\n", iter); stop_and_wait(); open_read(filename); continue; /* start over the while loop */ } else break; // we have done all tapes, ending the process } else { if(lu < 0) { switch(errno) { case EAGAIN: case EINTR: break; default: fprintf(stderr, "Error reading data: %s\n", strerror(errno)); stop_and_wait(); break; } continue; /* start over the while loop */ } } /* now we have data to write down */ offset = 0; do { ecru = write(1, buffer + offset, lu); if(ecru < 0) /* an error occured */ { int loop = 0; switch(errno) { case EAGAIN: case EINTR: loop = 1; break; default: fprintf(stderr, "Error writing data: %s\n", strerror(errno)); break; } if(loop) continue; /* start over the do - while loop */ else break; /* exiting the do - while loop, aborting the process */ } if(ecru > lu) { fprintf(stderr, "BUG MET at line %d\n", __LINE__); exit(ERR_BUG); /* end or processing */ } /* starting from here no error occurred and lu >= ecru >= 0 */ offset += ecru; lu -= ecru; } while(lu > 0); if(lu > 0) break; /* not all data could be written, aborting */ } if(fd >= 0) close(fd); if(buffer != NULL) free(buffer); } static int open_write(char *filename, int sync_mode) { int fd; int flag = sync_mode ? O_SYNC : 0; do { fd = open(filename, O_WRONLY|O_BINARY|flag); if(fd < 0) { fprintf(stderr,"Error opening output: %s\n", strerror(errno)); stop_and_wait(); } } while(fd < 0); return fd; } static int open_read(char *filename) { int fd; do { fd = open(filename, O_RDONLY|O_BINARY); if(fd < 0) { fprintf(stderr,"Error opening input: %s\n", strerror(errno)); stop_and_wait(); } } while(fd < 0); return fd; } static void init_timer(unsigned int bufsize, unsigned int rate) { if(rate > 0) { struct timeval tm_int; struct itimerval timer; /* program alarm() at correct rate */ signal(SIGALRM, alarm_handle); tm_int.tv_sec = bufsize / rate ; /* integer division */ tm_int.tv_usec = (bufsize % rate) * 1000000 / rate; /* remaining of the integer division expressed in microseconds */ timer.it_interval = tm_int; timer.it_value = tm_int; if(setitimer(ITIMER_REAL, &timer, NULL) != 0) { fprintf(stderr, "Cannot set timer to control transfer rate: %s\n", strerror(errno)); exit(ERR_TIMER); } can_go = 1; } } static void rate_limit(unsigned int rate) { if(rate > 0) { if(!can_go) pause(); /* waiting for a signal (ALARM) some time fraction of time to not exceed the expected transfer rate if the incoming flow is faster, the input buffer will fill up to a time the OS will suspend the writer, dar_split will have enough data to write and continue at its pace. The OS may awake the writer at a later time for before the dar_split input get empty */ can_go = 0; // will be set to 1 by the timer handle } } dar-2.7.15/src/dar_suite/command_line.cpp0000644000175000017500000045131314636067146015220 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #include "getopt_decision.h" #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if STDC_HEADERS # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #if HAVE_ERRNO_H #include #endif } // end extern "C" #include #include #include #include #include "command_line.hpp" #include "tools.hpp" #include "line_tools.hpp" #include "dar.hpp" #include "dar_suite.hpp" #include "no_comment.hpp" #include "config_file.hpp" #include "dar.hpp" #include "cygwin_adapt.hpp" #include "crit_action_cmd_line.hpp" #include "libdar.hpp" #include "fichier_local.hpp" #define OPT_STRING "c:A:x:d:t:l:v::z::y:nw::p::k::R:s:S:X:I:P:bhLWDru:U:VC:i:o:OT:E:F:K:J:Y:Z:B:fm:NH::a::eQG:M::g:#:*:,[:]:+:@:$:~:%:q/:^:_:01:2:.:3:9:<:>:=:4:5::6:7:8:{:}:j:\\:" #define ONLY_ONCE "Only one -%c is allowed, ignoring this extra option" #define MISSING_ARG "Missing argument to -%c option" #define INVALID_ARG "Invalid argument given to -%c option" #define INVALID_SIZE "Invalid size given with option -%c" #define INVALID_BS_FUNC "Syntax error in delta signature block length specification: %s" #define DEFAULT_CRYPTO_SIZE 10240 using namespace std; using namespace libdar; struct pre_mask { bool included; // whether it is a include or exclude entry string mask; // the string (either a mask or a filename) bool case_sensit; // whether comparison is case sensitive bool file_listing; // whether the corresponding string is a filename containing a list of file to match to bool glob_exp; // whether this is a glob (not regex) expression }; struct mask_opt { bool case_sensit; bool file_listing; const path & prefix; bool glob_exp; mask_opt(const path & ref) : prefix(ref) {}; void read_from(struct pre_mask m) { case_sensit = m.case_sensit; file_listing = m.file_listing; glob_exp = m.glob_exp; }; }; static const U_I min_compr_size_default = 100; // the default value for --mincompr static const U_I sparse_file_min_size_default = 15; // the default value for --sparse-file-min-size static const char * AUXILIARY_TARGET = "auxiliary"; static const char * REFERENCE_TARGET = "reference"; static void show_license(user_interaction & dialog); static void show_warranty(user_interaction & dialog); static void show_version(user_interaction & dialog, const char *command_name); static void usage(user_interaction & dialog, const char *command_name); static fsa_scope string_to_fsa(const string & arg); #if HAVE_GETOPT_LONG const struct option *get_long_opt(); #endif struct recursive_param { // input parameters shared_ptr dialog; const char *home; const deque dar_dcf_path; const deque dar_duc_path; // output parameters deque inclusions; deque name_include_exclude; deque path_include_exclude; deque ea_include_exclude; deque compr_include_exclude; deque backup_hook_include_exclude; bool readconfig; bool glob_mode; deque non_options; // list of user targets bool ordered_filters; bool case_sensit; bool fixed_date_mode; bool sparse_file_reactivation; U_I suffix_base; bool ea_erase; bool only_more_recent; bool detruire; bool no_inter; deque read_targets; // list of not found uset targets so far deque path_delta_include_exclude; bool duc_and; recursive_param(shared_ptr & x_dialog, const char *x_home, const deque & x_dar_dcf_path, const deque & x_dar_duc_path): dar_dcf_path(x_dar_dcf_path), dar_duc_path(x_dar_duc_path) { dialog = x_dialog; if(!dialog) throw Ememory("recursive_param::recursive_param"); home = x_home; // readconfig = true; glob_mode = true; // defaults to glob expressions ordered_filters = false; case_sensit = true; fixed_date_mode = false; sparse_file_reactivation = false; suffix_base = LINE_TOOLS_BIN_SUFFIX; ea_erase = false; only_more_recent = false; detruire = true; no_inter = false; duc_and = false; }; recursive_param(const recursive_param & ref): dar_dcf_path(ref.dar_dcf_path), dar_duc_path(ref.dar_duc_path) { throw SRC_BUG; } }; static bool get_args_recursive(recursive_param & rec, line_param & p, S_I argc, char * const argv[]); static void make_args_from_file(shared_ptr & dialog, operation op, const deque & targets, const string & filename, S_I & argc, char **&argv, deque & read_targets, // read targets are removed from this argument bool info_details); static void destroy(S_I argc, char **argv); static void skip_getopt(S_I argc, char *const argv[], S_I next_to_read); static bool update_with_config_files(recursive_param & rec, line_param & p); static mask *make_include_exclude_name(const string & x, mask_opt opt); static mask *make_exclude_path_ordered(const string & x, mask_opt opt); static mask *make_exclude_path_unordered(const string & x, mask_opt opt); static mask *make_include_path(const string & x, mask_opt opt); static mask *make_ordered_mask(deque & listing, mask *(*make_include_mask) (const string & x, mask_opt opt), mask *(*make_exclude_mask)(const string & x, mask_opt opt), const path & prefix); static mask *make_unordered_mask(deque & listing, mask *(*make_include_mask) (const string & x, mask_opt opt), mask *(*make_exclude_mask)(const string & x, mask_opt opt), const path & prefix); static void add_non_options(S_I argc, char * const argv[], deque & non_options); bool get_args(shared_ptr & dialog, const char *home, const deque & dar_dcf_path, const deque & dar_duc_path, S_I argc, char *const argv[], line_param & p) { string cmd = path(argv[0]).basename(); recursive_param rec = recursive_param(dialog, home, dar_dcf_path, dar_duc_path); // initializing parameters to their default values p.op = noop; p.file_size = 0; p.first_file_size = 0; p.filename = ""; p.allow_over = true; p.warn_over = true; p.info_details = false; p.display_treated = false; p.display_treated_only_dir = false; p.display_skipped = false; p.display_finished = false; p.display_masks = false; p.algo = compression::none; p.compression_level = 9; p.compression_block_size = 0; p.pause = 0; p.beep = false; p.empty_dir = false; p.input_pipe = ""; p.output_pipe = ""; p.what_to_check = comparison_fields::all; p.execute = ""; p.execute_ref = ""; p.pass.clear(); p.signatories.clear(); p.blind_signatures = false; p.pass_ref.clear(); p.flat = false; p.min_compr_size = min_compr_size_default; p.nodump = false; p.exclude_by_ea = false; p.ea_name_for_exclusion = ""; p.hourshift = 0; p.warn_remove_no_match = true; p.filter_unsaved = false; p.empty = false; p.alter_atime = true; p.same_fs = false; p.same_fs_incl.clear(); p.same_fs_excl.clear(); p.snapshot = false; p.cache_directory_tagging = false; p.crypto_size = DEFAULT_CRYPTO_SIZE; p.crypto_size_ref = DEFAULT_CRYPTO_SIZE; p.list_mode = archive_options_listing_shell::normal; p.aux_pass.clear(); p.aux_execute = ""; p.aux_crypto_size = DEFAULT_CRYPTO_SIZE; p.keep_compressed = false; p.fixed_date = 0; p.quiet = false; p.slice_perm = ""; p.slice_user = ""; p.slice_group = ""; p.repeat_count = 3; // 3 retry by default p.repeat_byte = 1; // 1 wasted byte allowed by default p.decremental = false; #if FURTIVE_READ_MODE_AVAILABLE p.furtive_read_mode = true; #else p.furtive_read_mode = false; #endif p.lax = false; p.use_sequential_marks = true; p.sequential_read = false; p.sparse_file_min_size = sparse_file_min_size_default; p.dirty = dirtyb_warn; p.security_check = true; p.user_comment = ""; p.hash = hash_algo::none; p.num_digits = 0; p.ref_num_digits = 0; p.aux_num_digits = 0; p.only_deleted = false; p.not_deleted = false; p.backup_hook_mask = nullptr; p.backup_hook_execute = ""; p.list_ea = false; p.ignore_unknown_inode = false; p.no_compare_symlink_date = true; p.scope = all_fsa_families(); p.multi_threaded_crypto = 0; p.multi_threaded_compress = 0; p.delta_sig = false; p.delta_mask = nullptr; p.delta_diff = true; p.delta_sig_min_size = 0; //< if zero is not modified, we will used the default value from libdar p.remote.clear(); p.ref_remote.clear(); p.aux_remote.clear(); p.remote_verbose = false; p.sizes_in_bytes = false; p.header_only = false; p.zeroing_neg_dates = false; p.ignored_as_symlink = ""; p.modet = modified_data_detection::mtime_size; p.iteration_count = 0; // will not touch the default API value if still set to zero p.kdf_hash = hash_algo::none; p.delta_sig_len.reset(); p.unix_sockets = false; p.in_place = false; if(!dialog) throw SRC_BUG; try { opterr = 0; if(!get_args_recursive(rec, p, argc, argv)) return false; // checking and updating options with configuration file if any if(rec.readconfig) if(! update_with_config_files(rec, p)) return false; // this cannot be done sooner, because "info_details" would always be equal to false // as command-line would not have been yet parsed. if(p.info_details) { if(!rec.non_options.empty()) { deque::iterator it = rec.non_options.begin(); bool init_message_done = false; while(it != rec.non_options.end()) { if(*it != AUXILIARY_TARGET && *it != REFERENCE_TARGET) // theses two targets are stored as "user targets" but are reserved targets not user's { if(!init_message_done) { dialog->message(gettext("User target found on command line or included file(s):")); init_message_done = true; } dialog->printf("\t%S", &(*it)); // yes, strange syntax, where "&(*it)" is not equal to "it" ... :-) } ++it; } if(!init_message_done) dialog->message(gettext("No user target found on command line")); } } // some sanity checks deque unseen = line_tools_substract_from_deque(rec.non_options, rec.read_targets); deque special_targets; special_targets.push_back(AUXILIARY_TARGET); special_targets.push_back(REFERENCE_TARGET); unseen = line_tools_substract_from_deque(unseen, special_targets); if(!unseen.empty()) { string not_seen; for(deque::iterator it = unseen.begin(); it != unseen.end(); ++it) not_seen += *it + " "; throw Erange("get_args", tools_printf(gettext("Given user target(s) could not be found: %S"), ¬_seen)); } if(p.filename == "" || p.sauv_root == nullptr || p.op == noop) throw Erange("get_args", tools_printf(gettext("Missing -c -x -d -t -l -C -+ option, see `%S -h' for help"), &cmd)); if(p.filename == "-" && !p.file_size.is_zero()) throw Erange("get_args", gettext("Slicing (-s option), is not compatible with archive on standard output (\"-\" as filename)")); if(p.filename != "-" && (p.op != create && p.op != isolate && p.op != merging)) if(p.sauv_root == nullptr) throw SRC_BUG; if(p.filename != "-") { bool creation = (p.op == create || p.op == isolate || p.op == merging || p.op == repairing); bool local = p.remote.ent_proto.empty(); if(local) { line_tools_check_basename(*dialog, *p.sauv_root, p.filename, EXTENSION, creation); if(!creation && p.num_digits.is_zero()) line_tools_check_min_digits(*dialog, *p.sauv_root, p.filename, EXTENSION, p.num_digits); } } if((p.op == merging || p.op == create) && p.aux_filename != nullptr) { if(p.aux_root == nullptr) throw SRC_BUG; else { line_tools_check_basename(*dialog, *p.aux_root, *p.aux_filename, EXTENSION, false); if(p.aux_num_digits.is_zero()) line_tools_check_min_digits(*dialog, *p.aux_root, *p.aux_filename, EXTENSION, p.aux_num_digits); } } if(p.in_place) { if(p.op != extract && p.op != diff) throw Erange("get_args", gettext("-ap/--alter=place option is only available for restoration and comparison")); if(p.fs_root != nullptr) throw Erange("get_args", gettext("-ap/--alter=place option is incompatible with -R/--fs-root option")); } if(p.fs_root == nullptr) { p.fs_root = new (nothrow) path("."); if(p.fs_root == nullptr) throw Ememory("get_args"); } if(rec.fixed_date_mode && p.op != create) throw Erange("get_args", gettext("-af option is only available with -c")); if(p.ref_filename != nullptr && p.op == listing) dialog->message(gettext("-A option is not available with -l")); if(p.list_mode != archive_options_listing_shell::normal && p.op != listing) dialog->message(gettext("-T option is only available with -l")); if(p.op == isolate && p.ref_filename == nullptr) throw Erange("get_args", gettext("with -C option, -A option is mandatory")); if(p.op == merging && p.ref_filename == nullptr) throw Erange("get_args", gettext("with -+ option, -A option is mandatory")); if(p.op != extract && !p.warn_remove_no_match) dialog->message(gettext("-wa is only useful with -x option")); if(p.filename == "-" && p.ref_filename != nullptr && *p.ref_filename == "-" && p.output_pipe == "" && !p.sequential_read) throw Erange("get_args", gettext("-o is mandatory when using \"-A -\" with \"-c -\" \"-C -\" or \"-+ -\"")); if(p.ref_filename != nullptr && *p.ref_filename != "-") { if(p.ref_root == nullptr) throw SRC_BUG; else { line_tools_check_basename(*dialog, *p.ref_root, *p.ref_filename, EXTENSION, false); if(p.ref_num_digits.is_zero()) line_tools_check_min_digits(*dialog, *p.ref_root, *p.ref_filename, EXTENSION, p.ref_num_digits); } } if(p.algo != compression::none && p.op != create && p.op != isolate && p.op != merging) dialog->message(gettext("-z option needs only to be used with -c -C or -+ options")); if(!p.first_file_size.is_zero() && p.file_size.is_zero()) throw Erange("get_args", gettext("-S option requires the use of -s")); if(p.what_to_check != comparison_fields::all && (p.op == isolate || (p.op == create && p.ref_root == nullptr) || p.op == test || p.op == listing || p.op == merging)) dialog->message(gettext("ignoring -O option, as it is useless in this situation")); if(p.execute_ref != "" && p.ref_filename == nullptr) dialog->message(gettext("-F is only useful with -A option, for the archive of reference")); if(p.pass_ref != "" && p.ref_filename == nullptr) { dialog->message(gettext("-J is only useful with -A option, for the archive of reference")); } if(p.flat && p.op != extract) dialog->message(gettext("-f in only available with -x option, ignoring")); if(p.min_compr_size != min_compr_size_default && p.op != create && p.op != merging) dialog->message(gettext("-m is only useful with -c")); if(!p.hourshift.is_zero()) { if(p.op == create) { if(p.ref_filename == nullptr) dialog->message(gettext("-H is only useful with -A option when making a backup")); } else if(p.op == extract) { if(!rec.only_more_recent) dialog->message(gettext("-H is only useful with -r option when extracting")); } else if(p.op != diff) dialog->message(gettext("-H is only useful with -c, -d or -x")); } if(p.filter_unsaved && p.op != listing) dialog->message(gettext("-as is only available with -l, ignoring -as option")); if(p.empty && p.op != create && p.op != extract && p.op != merging && p.op != test) dialog->message(gettext("-e is only useful with -x, -c or -+ options")); if(!p.alter_atime && p.op != create && p.op != diff) dialog->message(gettext("-ac is only useful with -c or -d")); if(p.same_fs || ! p.same_fs_incl.empty() || ! p.same_fs_excl.empty()) { if(p.op != create) dialog->message(gettext("-M is only useful with -c")); } if(p.snapshot && p.op != create) dialog->message(gettext("The snapshot backup (-A +) is only available with -c option, ignoring")); if(p.cache_directory_tagging && p.op != create) dialog->message(gettext("The Cache Directory Tagging Standard is only useful while performing a backup, ignoring it here")); if((p.aux_root != nullptr || p.aux_filename != nullptr) && p.op != merging && p.op != create) throw Erange("get_args", gettext("-@ is only available with -+ and -c options")); if(p.aux_pass != "" && p.op != merging && p.op != create) throw Erange("get_args", gettext("-$ is only available with -+ option and -c options")); if(p.aux_execute != "" && p.op != merging && p.op != create) throw Erange("get_args", gettext("-~ is only available with -+ and -c options")); if(p.aux_crypto_size != DEFAULT_CRYPTO_SIZE && p.op != merging && p.op != create) throw Erange("get_args", tools_printf(gettext("-%% is only available with -+ option"))); if(p.aux_pass != "" && p.aux_filename == nullptr) dialog->message(gettext("-$ is only useful with -@ option, for the auxiliary archive of reference")); if(p.aux_crypto_size != DEFAULT_CRYPTO_SIZE && p.aux_filename == nullptr) dialog->printf(gettext("-%% is only useful with -@ option, for the auxiliary archive of reference")); if(p.aux_execute != "" && p.aux_filename == nullptr) dialog->message(gettext("-~ is only useful with -@ option, for the auxiliary archive of reference")); if(p.keep_compressed && p.op != merging) { dialog->message(gettext("-ak is only available while merging (operation -+), ignoring -ak")); p.keep_compressed = false; } if(p.algo != compression::none && p.op == merging && p.keep_compressed) dialog->message(gettext("Compression option (-z option) is useless and ignored when using -ak option")); // sparse files handling if(p.sparse_file_min_size != sparse_file_min_size_default) { if(p.op != merging && p.op != create) dialog->message(gettext("--sparse-file-min-size only available while saving or merging archives, ignoring")); else if(p.op == merging && !rec.sparse_file_reactivation) dialog->message(gettext("To use --sparse-file-min-size while merging archive, you need to use -ah option too, please check man page for details")); } if(p.op == merging && !rec.sparse_file_reactivation) p.sparse_file_min_size = 0; // disabled by default for archive merging if((p.not_deleted || p.only_deleted) && p.op != extract) dialog->message(gettext("-k option is only useful with -x option")); if(p.not_deleted && p.only_deleted) throw Erange("get_args", gettext("-konly and -kignore cannot be used at the same time")); if(rec.no_inter && !p.pause.is_zero()) throw Erange("get_args", gettext("-p and -Q options are mutually exclusives")); if(p.display_finished && p.op != create) dialog->message(gettext("-vf is only useful with -c option")); if(p.unix_sockets && p.op != extract) dialog->message(gettext("-au is only useful with -c option")); if(p.op == repairing) { if(p.ref_filename == nullptr) throw Erange("get_args", gettext("-A option is required with -y option")); if(p.snapshot) throw Erange("get_args", gettext("'-A +' is not possible with -y option")); if(rec.fixed_date_mode) throw Erange("get_args", gettext("-af is not possible with -y option")); if(p.only_deleted) throw Erange("get_args", gettext("-k option is not possible with -y option")); if(rec.name_include_exclude.size() > 0 || rec.path_include_exclude.size() > 0) throw Erange("get_args", gettext("-X, -I, -P, -g, -], -[ and any other file selection relative commands are not possible with -y option")); if(p.empty_dir) dialog->message(gettext("-D option is useless with -y option")); if(rec.only_more_recent) dialog->message(gettext("-r option is useless with -y option")); if(rec.ea_include_exclude.size() > 0) throw Erange("get_args", gettext("-u, -U, -P, -g, -], -[ and any other EA selection relative commands are not possible with -y option")); if(p.what_to_check != comparison_fields::all) throw Erange("get_args", gettext("-O option is not possible with -y option")); if(!p.hourshift.is_zero()) dialog->message(gettext("-H option is useless with -y option")); if(p.filter_unsaved) dialog->message(gettext("-as option is useless with -y option")); if(rec.ea_erase) dialog->message(gettext("-ae option is useless with -y option")); if(p.decremental) dialog->message(gettext("-ad option is useless with -y option")); if(!p.security_check) dialog->message(gettext("-asecu option is useless with -y option")); if(p.ignore_unknown_inode) dialog->message(gettext("-ai option is useless with -y option")); if(!p.no_compare_symlink_date) dialog->message(gettext("--alter=do-not-compare-symlink-mtime option is useless with -y option")); if(p.same_fs || ! p.same_fs_incl.empty() || ! p.same_fs_excl.empty()) dialog->message(gettext("-M option is useless with -y option")); if(p.aux_filename != nullptr) dialog->message(gettext("-@ option is useless with -y option")); if(p.overwrite != nullptr) dialog->message(gettext("-/ option is useless with -y option")); if(rec.backup_hook_include_exclude.size() > 0) dialog->message(gettext("-< and -> options are useless with -y option")); if(p.ea_name_for_exclusion != "") dialog->message(gettext("-5 option is useless with -y option")); if(p.delta_sig || !p.delta_diff) dialog->message(gettext("-8 option is useless with -y option")); if(rec.path_delta_include_exclude.size() > 0) dialog->message(gettext("-{ and -} options are useless with -y option")); if(p.ignored_as_symlink != "") dialog->message(gettext("-\\ option is useless with -y option")); if(p.algo != compression::none) dialog->message(gettext("compression (-z option) cannot be changed with -y option")); if(p.keep_compressed) dialog->message(gettext("-ak option is useless with -y option")); if(p.sparse_file_min_size != sparse_file_min_size_default) dialog->message(gettext("-ah option is useless with -y option")); if(p.sequential_read) throw Erange("get_args", gettext("--sequential-read is useless with -y option")); if(!p.use_sequential_marks) throw Erange("get_args", gettext("--alter=tape-marks is impossible with -y option")); } ////////////////////// // generating masks // for filenames // string cwd = tools_getcwd(); if(rec.ordered_filters) p.selection = make_ordered_mask(rec.name_include_exclude, &make_include_exclude_name, &make_include_exclude_name, tools_relative2absolute_path(*p.fs_root, cwd)); else // unordered filters p.selection = make_unordered_mask(rec.name_include_exclude, &make_include_exclude_name, &make_include_exclude_name, tools_relative2absolute_path(*p.fs_root, cwd)); ///////////////////////// // generating masks for // directory tree // if(rec.ordered_filters) p.subtree = make_ordered_mask(rec.path_include_exclude, &make_include_path, &make_exclude_path_ordered, p.op != test && p.op != merging && p.op != listing ? tools_relative2absolute_path(*p.fs_root, cwd) : PSEUDO_ROOT); else // unordered filters p.subtree = make_unordered_mask(rec.path_include_exclude, &make_include_path, &make_exclude_path_unordered, p.op != test && p.op != merging && p.op != listing ? tools_relative2absolute_path(*p.fs_root, cwd) : PSEUDO_ROOT); //////////////////////////////// // generating mask for // compression selected files // if(p.algo == compression::none) { if(!rec.compr_include_exclude.empty()) dialog->message(gettext("-Y and -Z are only useful with compression (-z option), ignoring any -Y and -Z option")); if(p.min_compr_size != min_compr_size_default) dialog->message(gettext("-m is only useful with compression (-z option), ignoring -m")); } if(p.algo != compression::none) if(rec.ordered_filters) p.compress_mask = make_ordered_mask(rec.compr_include_exclude, &make_include_exclude_name, &make_include_exclude_name, tools_relative2absolute_path(*p.fs_root, cwd)); else p.compress_mask = make_unordered_mask(rec.compr_include_exclude, &make_include_exclude_name, &make_include_exclude_name, tools_relative2absolute_path(*p.fs_root, cwd)); else { p.compress_mask = new (nothrow) bool_mask(true); if(p.compress_mask == nullptr) throw Ememory("get_args"); } //////////////////////////////// // generating mask for EA // // if(rec.ordered_filters) p.ea_mask = make_ordered_mask(rec.ea_include_exclude, &make_include_exclude_name, &make_include_exclude_name, tools_relative2absolute_path(*p.fs_root, cwd)); else // unordered filters p.ea_mask = make_unordered_mask(rec.ea_include_exclude, &make_include_exclude_name, &make_include_exclude_name, tools_relative2absolute_path(*p.fs_root, cwd)); //////////////////////////////// // generating mask for backup hook // // if(rec.backup_hook_include_exclude.size() == 0) { p.backup_hook_mask = nullptr; if(p.backup_hook_execute != "") { p.backup_hook_execute = ""; if(p.op != create) dialog->message(gettext("-= option is valid only while saving files, thus in conjunction with -c option, ignoring")); else dialog->message(gettext("-= option will be ignored as it is useless if you do not specify to which files or directories this backup hook is to be applied, thanks to -< and -> options. See man page for more details.")); } } else if(p.op != create) { dialog->message(gettext("backup hook feature (-<, -> or -= options) is only available when saving files, ignoring")); p.backup_hook_mask = nullptr; p.backup_hook_execute = ""; } else if(rec.ordered_filters) p.backup_hook_mask = make_ordered_mask(rec.backup_hook_include_exclude, &make_exclude_path_unordered, // no mistake here about *exclude*, nor *unordered* &make_exclude_path_unordered, // no mistake here about *exclude*, nor *unordered* tools_relative2absolute_path(*p.fs_root, cwd)); else p.backup_hook_mask = make_unordered_mask(rec.backup_hook_include_exclude, &make_exclude_path_unordered,// no mistake here about *exclude* &make_exclude_path_unordered, tools_relative2absolute_path(*p.fs_root, cwd)); //////////////////////////////// // generating mask for // delta signatures // if(rec.path_delta_include_exclude.size() > 0) { if(rec.ordered_filters) p.delta_mask = make_ordered_mask(rec.path_delta_include_exclude, &make_exclude_path_unordered, // no mistake here about *exclude*, nor *unordered* &make_exclude_path_unordered, // no mistake here about *exclude*, nor *unordered* p.op != test && p.op != merging && p.op != listing && p.op != isolate ? tools_relative2absolute_path(*p.fs_root, tools_getcwd()) : PSEUDO_ROOT); else // unordered filters p.delta_mask = make_unordered_mask(rec.path_delta_include_exclude, &make_exclude_path_unordered, // no mistake here about *exclude* &make_exclude_path_unordered, // no mistake here about *exclude* p.op != test && p.op != merging && p.op != listing && p.op != isolate ? tools_relative2absolute_path(*p.fs_root, tools_getcwd()) : PSEUDO_ROOT); } //////////////////////////////// // overwriting policy // switch(p.op) { case merging: if(p.overwrite == nullptr) { p.overwrite = new (nothrow) crit_constant_action(data_preserve, EA_merge_preserve); if(p.overwrite == nullptr) throw Ememory("get_args"); } break; case extract: if(p.overwrite == nullptr) { line_tools_4_4_build_compatible_overwriting_policy(p.allow_over, rec.detruire, rec.only_more_recent, p.hourshift, rec.ea_erase, p.overwrite); if(p.overwrite == nullptr) throw Ememory("get_args"); } break; default: if(p.overwrite != nullptr) { delete p.overwrite; p.overwrite = nullptr; dialog->message(gettext("-/ option is only useful with -+ option, ignoring")); } } //////////////////////////////// // user comment // // if(p.user_comment != "") if(p.op != create && p.op != merging && p.op != isolate) dialog->message(gettext("-. option is only useful when merging, creating or isolating an archive, ignoring")); else { p.user_comment = line_tools_expand_user_comment(p.user_comment, argc, argv); if(p.info_details) dialog->printf(gettext("The following user comment will be placed in clear text in the archive: %S"), &p.user_comment); } else p.user_comment = "N/A"; //////////////////////////////// // security check // // if(!p.alter_atime) p.security_check = false; //////////////////////////////// // multi-thread helpers // // if(p.compression_block_size == 0 && p.multi_threaded_compress > 1 && p.algo != compression::none) p.compression_block_size = 240 * 1024; if(compile_time::libthreadar() && p.multi_threaded_crypto == 0) p.multi_threaded_crypto = 2; } catch(Erange & e) { dialog->message(string(gettext("Parse error: ")) + e.get_message()); return false; } return true; } const char *get_short_opt() { return OPT_STRING; } static bool get_args_recursive(recursive_param & rec, line_param & p, S_I argc, char * const argv[]) { S_I lu; S_I rec_c; char **rec_v = nullptr; pre_mask tmp_pre_mask; U_I tmp; string tmp_string, tmp_string2; infinint tmp_infinint; // fetching first the targets (non optional argument of command line) add_non_options(argc, argv, rec.non_options); #if HAVE_GETOPT_LONG while((lu = getopt_long(argc, argv, OPT_STRING, get_long_opt(), nullptr)) != EOF) #else while((lu = getopt(argc, argv, OPT_STRING)) != EOF) #endif { switch(lu) { case 'c': case 'x': case 'd': case 't': case 'l': case 'C': case '+': case 'y': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.filename != "" || p.sauv_root != nullptr) throw Erange("get_args", gettext(" Only one option of -c -d -t -l -C -x -+ or -y is allowed")); if(string(optarg) != string("")) { string path_basename; if(line_tools_split_entrepot_path(optarg, p.remote.ent_proto, p.remote.ent_login, p.remote.ent_pass, p.remote.ent_host, p.remote.ent_port, path_basename)) line_tools_split_path_basename(path_basename.c_str(), p.sauv_root, p.filename); else { p.remote.clear(); line_tools_split_path_basename(optarg, p.sauv_root, p.filename); } } else throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); switch(lu) { case 'c': p.op = create; break; case 'x': p.op = extract; break; case 'd': p.op = diff; break; case 't': p.op = test; break; case 'l': p.op = listing; break; case 'C': p.op = isolate; break; case '+': p.op = merging; break; case 'y': p.op = repairing; break; default: throw SRC_BUG; } break; case 'A': if(p.ref_filename != nullptr || p.ref_root != nullptr || p.snapshot || !p.fixed_date.is_zero()) throw Erange("get_args", gettext("Only one -A option is allowed")); if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(strcmp("", optarg) == 0) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); if(rec.fixed_date_mode) { try { try { // trying to read a simple integer // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci tmp = string(optarg); p.fixed_date = tmp.computer(); } catch(Edeci & e) { // fallback to human readable string p.fixed_date = line_tools_convert_date(optarg); } } catch(Egeneric & e) { throw Erange("get_args", string(gettext("Error while parsing -A argument as a date: ")+ e.get_message())); } } else if(strcmp("+", optarg) == 0) p.snapshot = true; else { p.ref_filename = new (nothrow) string(); if(p.ref_filename == nullptr) throw Ememory("get_args"); try { string path_basename; if(line_tools_split_entrepot_path(optarg, p.ref_remote.ent_proto, p.ref_remote.ent_login, p.ref_remote.ent_pass, p.ref_remote.ent_host, p.ref_remote.ent_port, path_basename)) line_tools_split_path_basename(path_basename.c_str(), p.ref_root, *p.ref_filename); else { p.ref_remote.clear(); line_tools_split_path_basename(optarg, p.ref_root, *p.ref_filename); rec.non_options.push_back("reference"); } } catch(...) { delete p.ref_filename; p.ref_filename = nullptr; throw; } } break; case 'v': if(optarg == nullptr) { p.info_details = true; p.display_treated = true; p.display_treated_only_dir = false; } else if(strcasecmp("skipped", optarg) == 0 || strcasecmp("s", optarg) == 0) p.display_skipped = true; else if(strcasecmp("treated", optarg) == 0 || strcasecmp("t", optarg) == 0) { p.display_treated = true; p.display_treated_only_dir = false; } else if(strcasecmp("messages", optarg) == 0 || strcasecmp("m", optarg) == 0) p.info_details = true; else if(strcasecmp("dir", optarg) == 0 || strcasecmp("d", optarg) == 0) { p.display_treated = true; p.display_treated_only_dir = true; } else if(strcasecmp("finished", optarg) == 0 || strcasecmp("f", optarg) == 0) p.display_finished = true; else if(strcasecmp("all", optarg) == 0 || strcasecmp("a", optarg) == 0) { p.info_details = true; p.display_skipped = true; p.display_treated = true; p.display_treated_only_dir = false; } else if(strcasecmp("masks", optarg) == 0) p.display_masks = true; else throw Erange("command_line.cpp:get_args_recursive", tools_printf(gettext(INVALID_ARG), char(lu))); break; case 'z': if(optarg != nullptr) line_tools_split_compression_algo(optarg, rec.suffix_base, p.algo, p.compression_level, p.compression_block_size); else if(p.algo == compression::none) p.algo = compression::gzip; else throw Erange("get_args", gettext("Choose only one compression algorithm")); break; case 'n': p.allow_over = false; if(!p.warn_over) { rec.dialog->message(gettext("-w option is useless with -n")); p.warn_over = false; } break; case 'w': p.warn_over = false; if(optarg != nullptr) { if(strcmp(optarg, "a") == 0 || strcmp(optarg, "all") == 0) p.warn_remove_no_match = false; else if(strcmp(optarg, "d") != 0 && strcmp(optarg, "default") != 0) throw Erange("get_args", string(gettext("Unknown argument given to -w: ")) + optarg); // else this is the default -w } break; case 'p': if(optarg != nullptr) { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci conv = string(optarg); p.pause = conv.computer(); } else p.pause = 1; break; case 'k': if(optarg == nullptr) // -k without argument { if(p.only_deleted) throw Erange("command_line.cpp:get_args_recursive", string(gettext("\"-k\" (or \"-kignore\") and \"-konly\" are not compatible"))); p.not_deleted = true; } else if(strcasecmp(optarg, "ignore") == 0) { if(p.only_deleted) throw Erange("command_line.cpp:get_args_recursive", string(gettext("\"-k\" (or \"-kignore\") and \"-konly\" are not compatible"))); p.not_deleted = true; } else if(strcasecmp(optarg, "only") == 0) { if(p.not_deleted) throw Erange("command_line.cpp:get_args_recursive", string(gettext("\"-k\" (or \"-kignore\") and \"-konly\" are not compatible"))); p.only_deleted = true; } else throw Erange("command_line.cpp:get_args_recursive", tools_printf(gettext("Unknown argument given to -k : %s"), optarg)); break; case 'R': if(p.fs_root != nullptr) throw Erange("get_args", gettext("Only one -R option is allowed")); if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); else { try { // first, trying to read the path as if it was a UNIX path // this is necessary to take care of dot (.) and double dot (..) // part inside the path and eventually reduce them to be able // to apply filters on it in a consistent manner p.fs_root = new (nothrow) path(optarg, false); } catch(Erange & e) { // well, it is not a UNIX path, using undisclosed object creation mode // for example argument may contain // or \\ this tolerance has been // added at release 2.4.0, but has drawback when using Unix path beginning // with a dot ./restore (bug reported by Jim Avera against release 2.5.6) p.fs_root = new (nothrow) path(optarg, true); } } if(p.fs_root == nullptr) throw Ememory("get_args"); break; case 's': if(!p.file_size.is_zero()) throw Erange("get_args", gettext("Only one -s option is allowed")); if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); else { try { p.file_size = tools_get_extended_size(optarg, rec.suffix_base); if(p.first_file_size.is_zero()) p.first_file_size = p.file_size; } catch(Edeci &e) { rec.dialog->message(tools_printf(gettext(INVALID_SIZE), char(lu))); return false; } } break; case 'S': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.first_file_size.is_zero()) p.first_file_size = tools_get_extended_size(optarg, rec.suffix_base); else if(p.file_size.is_zero()) throw Erange("get_args", gettext("Only one -S option is allowed")); else if(p.file_size == p.first_file_size) { try { p.first_file_size = tools_get_extended_size(optarg, rec.suffix_base); if(p.first_file_size == p.file_size) rec.dialog->message(gettext("Giving to -S option the same value as the one given to -s option is useless")); } catch(Egeneric &e) { rec.dialog->message(tools_printf(gettext(INVALID_SIZE), char(lu))); return false; } } else throw Erange("get_args", gettext("Only one -S option is allowed")); break; case 'X': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = false; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.name_include_exclude.push_back(tmp_pre_mask); break; case 'I': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = true; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.name_include_exclude.push_back(tmp_pre_mask); break; case 'P': case ']': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = lu == ']'; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = false; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.path_include_exclude.push_back(tmp_pre_mask); break; case 'g': case '[': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = lu == '['; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = true; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.path_include_exclude.push_back(tmp_pre_mask); break; case 'b': p.beep = true; break; case 'h': usage(*rec.dialog, argv[0]); p.op = version_or_help; return false; case 'L': show_license(*rec.dialog); return false; case 'W': show_warranty(*rec.dialog); return false; case 'D': if(p.empty_dir) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else p.empty_dir = true; break; case 'r': if(!p.allow_over) rec.dialog->message(gettext("-r is useless with -n")); if(rec.only_more_recent) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else rec.only_more_recent = true; break; case 'u': case 'U': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = lu == 'U'; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.ea_include_exclude.push_back(tmp_pre_mask); break; case 'V': show_version(*rec.dialog, argv[0]); p.op = version_or_help; return false; case 'i': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.input_pipe == "") p.input_pipe = optarg; else rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); break; case 'o': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.output_pipe == "") p.output_pipe = optarg; else rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); break; case 'O': if(p.what_to_check != comparison_fields::all) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else if(optarg == nullptr) p.what_to_check = comparison_fields::ignore_owner; else if(strcasecmp(optarg, "ignore-owner") == 0) p.what_to_check = comparison_fields::ignore_owner; else if(strcasecmp(optarg, "mtime") == 0) p.what_to_check = comparison_fields::mtime; else if(strcasecmp(optarg, "inode-type") == 0) p.what_to_check = comparison_fields::inode_type; else throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); break; case 'T': if(p.op == create || p.op == merging || p.op == isolate) { // this is the --kdf-iter-count option if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); vector splitted; string iter_count; string kdf_hash; line_tools_split(optarg, ':', splitted); switch(splitted.size()) { case 1: iter_count = optarg; kdf_hash = ""; break; case 2: iter_count = splitted[0]; kdf_hash = splitted[1]; break; default: throw Erange("get_args", tools_printf(gettext("Invalid argument given to -T option, expecting [:]"))); } try { p.iteration_count = tools_get_extended_size(iter_count, rec.suffix_base); } catch(Edeci &e) { rec.dialog->message(tools_printf(gettext(INVALID_SIZE), char(lu))); return false; } if(kdf_hash != "") { if(!string_to_hash_algo(kdf_hash, p.kdf_hash) || p.kdf_hash == hash_algo::none) throw Erange("get_args", tools_printf(gettext("Invalid hash algorithm provided to -T opton: %s"), kdf_hash.c_str())); } } else { // using the legacy meaning of 'T' option (--tree-format) in absence of context information if(optarg == nullptr) p.list_mode = archive_options_listing_shell::tree; else if(strcasecmp("normal", optarg) == 0) p.list_mode = archive_options_listing_shell::normal; else if(strcasecmp("tree", optarg) == 0) p.list_mode = archive_options_listing_shell::tree; else if(strcasecmp("xml", optarg) == 0) p.list_mode = archive_options_listing_shell::xml; else if(strcasecmp("slicing", optarg) == 0 || strcasecmp("slice", optarg) == 0) p.list_mode = archive_options_listing_shell::slicing; else throw Erange("command_line.cpp:get_args_recursive", tools_printf(gettext(INVALID_ARG), char(lu))); } break; case 'E': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); line_tools_split_at_first_space(optarg, tmp_string, tmp_string2); tmp_string = line_tools_get_full_path_from_PATH(rec.dar_duc_path, tmp_string.c_str()) + " " + tmp_string2; if(p.execute == "") p.execute = tmp_string; else { if(rec.duc_and) p.execute += string(" && "); else p.execute += string(" ; "); p.execute += tmp_string; } break; case 'F': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); line_tools_split_at_first_space(optarg, tmp_string, tmp_string2); tmp_string = line_tools_get_full_path_from_PATH(rec.dar_duc_path, tmp_string.c_str()) + " " + tmp_string2; if(p.execute_ref == "") p.execute_ref = tmp_string; else { if(rec.duc_and) p.execute_ref += string(" && "); else p.execute_ref += string(" ; "); p.execute_ref += tmp_string; } break; case 'J': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.pass_ref == "") p.pass_ref = secu_string(optarg, strlen(optarg)); else rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); break; case 'K': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.pass == "") p.pass = secu_string(optarg, strlen(optarg)); else rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); break; case 'Y': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = true; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.compr_include_exclude.push_back(tmp_pre_mask); break; case 'Z': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = false; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.compr_include_exclude.push_back(tmp_pre_mask); break; case 'B': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_string = line_tools_get_full_path_from_PATH(rec.dar_dcf_path, optarg); if(find(rec.inclusions.begin(), rec.inclusions.end(), tmp_string) != rec.inclusions.end()) throw Erange("get_args", tools_printf(gettext("File inclusion loop detected. The file %s includes itself directly or through other files (-B option)"), optarg)); else { bool ret; try { make_args_from_file(rec.dialog, p.op, rec.non_options, tmp_string, rec_c, rec_v, rec.read_targets, p.info_details); } catch(Esystem & e) { Erange modif = Erange("get_args", tools_printf(gettext("Error reading included file (%s): "), optarg) + e.get_message()); throw modif; } catch(Erange & e) { Erange modif = Erange("get_args", tools_printf(gettext("Error in included file (%s): "), optarg) + e.get_message()); throw modif; } S_I optind_mem = line_tools_reset_getopt(); // save the external variable to use recursivity (see getopt) // reset getopt module try { rec.inclusions.push_back(tmp_string); try { ret = get_args_recursive(rec, p, rec_c, rec_v); } catch(Erange & e) { Erange more = Erange(e.get_source(), tools_printf(gettext("In included file %S: "), &tmp_string) + e.get_message()); rec.inclusions.pop_back(); throw more; } catch(...) { rec.inclusions.pop_back(); throw; } rec.inclusions.pop_back(); } catch(...) { destroy(rec_c, rec_v); rec_v = nullptr; rec_c = 0; skip_getopt(argc, argv, optind_mem); throw; } destroy(rec_c, rec_v); rec_v = nullptr; rec_c = 0; skip_getopt(argc, argv, optind_mem); // restores getopt after recursion if(!ret) return false; } break; case 'f': if(p.flat) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else p.flat = true; break; case 'm': if(p.min_compr_size != min_compr_size_default) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else { if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); p.min_compr_size = tools_get_extended_size(optarg, rec.suffix_base); if(p.min_compr_size == min_compr_size_default) rec.dialog->message(tools_printf(gettext("%d is the default value for -m, no need to specify it on command line, ignoring"), min_compr_size_default)); break; } case 'N': if(!rec.readconfig) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else rec.readconfig = false; break; case ' ': #ifdef LIBDAR_NODUMP_FEATURE if(p.nodump) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else p.nodump = true; break; #else throw Ecompilation(gettext("--nodump feature has not been activated at compilation time, it is thus not available")); #endif case 'H': if(optarg == nullptr) p.hourshift = 1; else { try { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) p.hourshift = libdar::deci(string(optarg)).computer(); } catch(Edeci & e) { throw Erange("command_line.cpp:get_args_recursive", gettext("Argument given to -H is not a positive integer number")); } } break; case 'a': if(optarg == nullptr) throw Erange("command_line.cpp:get_args_recursive", gettext("-a option requires an argument")); if(strcasecmp("SI-unit", optarg) == 0 || strcasecmp("SI", optarg) == 0 || strcasecmp("SI-units", optarg) == 0) rec.suffix_base = LINE_TOOLS_SI_SUFFIX; else if(strcasecmp("binary-unit", optarg) == 0 || strcasecmp("binary", optarg) == 0 || strcasecmp("binary-units", optarg) == 0) rec.suffix_base = LINE_TOOLS_BIN_SUFFIX; else if(strcasecmp("atime", optarg) == 0 || strcasecmp("a", optarg) == 0) { p.alter_atime = true; p.furtive_read_mode = false; } else if(strcasecmp("ctime", optarg) == 0 || strcasecmp("c", optarg) == 0) { p.alter_atime = false; p.furtive_read_mode = false; } else if(strcasecmp("m", optarg) == 0 || strcasecmp("mask", optarg) == 0) { if(rec.ordered_filters) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else rec.ordered_filters = true; } else if(strcasecmp("n", optarg) == 0 || strcasecmp("no-case", optarg) == 0 || strcasecmp("no_case", optarg) == 0) rec.case_sensit = false; else if(strcasecmp("case", optarg) == 0) rec.case_sensit = true; else if(strcasecmp("s", optarg) == 0 || strcasecmp("saved", optarg) == 0) { if(p.filter_unsaved) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else p.filter_unsaved = true; } else if(strcasecmp("e", optarg) == 0 || strcasecmp("erase_ea", optarg) == 0) { if(rec.ea_erase) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else rec.ea_erase = true; } else if(strcasecmp("g", optarg) == 0 || strcasecmp("glob", optarg) == 0) rec.glob_mode = true; else if(strcasecmp("r", optarg) == 0 || strcasecmp("regex", optarg) == 0) rec.glob_mode = false; else if(strcasecmp("k", optarg) == 0 || strcasecmp("keep-compressed", optarg) == 0) { if(p.keep_compressed) rec.dialog->message(gettext("-ak option need not be specified more than once, ignoring extra -ak options")); p.keep_compressed = true; } else if(strcasecmp("f", optarg) == 0 || strcasecmp("fixed-date", optarg) == 0) { if(p.ref_filename != nullptr || p.ref_root != nullptr || p.snapshot) throw Erange("get_args", gettext("-af must be present before -A option not after!")); if(rec.fixed_date_mode) rec.dialog->message(gettext("-af option need not be specified more than once, ignoring extra -af options")); rec.fixed_date_mode = true; } else if(strcasecmp("d", optarg) == 0 || strcasecmp("decremental", optarg) == 0) p.decremental = true; else if(strcasecmp("l", optarg) == 0 || strcasecmp("lax", optarg) == 0) p.lax = true; else if(strcasecmp("t", optarg) == 0 || strcasecmp("tape-marks", optarg) == 0) p.use_sequential_marks = false; else if(strcasecmp("h", optarg) == 0 || strcasecmp("holes-recheck", optarg) == 0) rec.sparse_file_reactivation = true; else if(strcasecmp("secu", optarg) == 0) p.security_check = false; else if(strcasecmp("list-ea", optarg) == 0) p.list_ea = true; else if(strcasecmp("i", optarg) == 0 || strcasecmp("ignore-unknown-inode-type", optarg) == 0) p.ignore_unknown_inode = true; else if(strcasecmp("do-not-compare-symlink-mtime", optarg) == 0) p.no_compare_symlink_date = false; else if(strcasecmp("test-self-reported-bug", optarg) == 0) throw SRC_BUG; // testing the way a internal error is reported else if(strcasecmp("b", optarg) == 0 || strcasecmp("blind-to-signatures", optarg) == 0) p.blind_signatures = true; else if(strcasecmp("duc", optarg) == 0) rec.duc_and = true; else if(strcasecmp("file-auth", optarg) == 0 || strcasecmp("file-authentication", optarg) == 0) { p.remote.auth_from_file = true; p.ref_remote.auth_from_file = true; p.aux_remote.auth_from_file = true; } else if(strcasecmp("y", optarg) == 0 || strcasecmp("byte", optarg) == 0 || strcasecmp("bytes", optarg) == 0) p.sizes_in_bytes = true; else if(strcasecmp("header", optarg) == 0) p.header_only = true; else if(strcasecmp("z", optarg) == 0 || strcasecmp("zeroing-negative-dates", optarg) == 0) p.zeroing_neg_dates = true; else if(strcasecmp("u", optarg) == 0 || strcasecmp("unix-sockets", optarg) == 0) p.unix_sockets = true; else if(strcasecmp("p", optarg) == 0 || strcasecmp("place", optarg) == 0) p.in_place = true; else if(strcasecmp("vc", optarg) == 0 || strcasecmp("verbose-libcurl", optarg) == 0) p.remote_verbose = true; else throw Erange("command_line.cpp:get_args_recursive", tools_printf(gettext("Unknown argument given to -a : %s"), optarg)); break; case 'e': if(p.empty) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else p.empty = true; break; case 'Q': rec.no_inter = true; break; case 'G': if(optarg == nullptr) throw Erange("command_line.cpp:get_arg_recursive", tools_printf(gettext(INVALID_ARG), char(lu))); if(compile_time::libthreadar()) { deque split; line_tools_split(string(optarg), ',' , split); switch(split.size()) { case 1: if(! tools_my_atoi(optarg, tmp)) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); else { if(tmp < 1) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); p.multi_threaded_crypto = 2; p.multi_threaded_compress = (U_I)tmp; } break; case 2: if(! tools_my_atoi(split[0].c_str(), tmp)) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); else { if(tmp < 1) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); p.multi_threaded_crypto = (U_I)tmp; } if(! tools_my_atoi(split[1].c_str(), tmp)) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); else { if(tmp < 1) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); p.multi_threaded_compress = (U_I)tmp; } break; default: throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); } } else throw Ecompilation(gettext("libthreadar is required for multithreaded execution")); break; case 'M': if(optarg == nullptr) // the legacy -M option { if(p.same_fs) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else p.same_fs = true; } else // the 2.7.0 new option form { deque splitted; line_tools_split(string(optarg), ':', splitted); if(splitted.size() != 2 || (splitted[0] != "X" && splitted[0] != "I")) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); if(splitted[0] == "I") p.same_fs_incl.push_back(splitted[1]); else p.same_fs_excl.push_back(splitted[1]); } break; case '#': if(! tools_my_atoi(optarg, tmp)) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); else p.crypto_size = (U_32)tmp; break; case '*': if(! tools_my_atoi(optarg, tmp)) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); else p.crypto_size_ref = (U_32)tmp; break; case ',': if(p.cache_directory_tagging) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else p.cache_directory_tagging = true; break; case '@': if(p.aux_filename != nullptr || p.aux_root != nullptr) throw Erange("get_args", gettext("Only one -@ option is allowed")); if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(strcmp("", optarg) == 0) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); else { p.aux_filename = new (nothrow) string(); if(p.aux_filename == nullptr) throw Ememory("get_args"); try { string path_basename; if(line_tools_split_entrepot_path(optarg, p.aux_remote.ent_proto, p.aux_remote.ent_login, p.aux_remote.ent_pass, p.aux_remote.ent_host, p.aux_remote.ent_port, path_basename)) line_tools_split_path_basename(path_basename.c_str(), p.aux_root, *p.aux_filename); else { p.aux_remote.clear(); line_tools_split_path_basename(optarg, p.aux_root, *p.aux_filename); rec.non_options.push_back("auxiliary"); } } catch(...) { delete p.aux_filename; p.aux_filename = nullptr; throw; } } break; case '~': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); line_tools_split_at_first_space(optarg, tmp_string, tmp_string2); tmp_string = line_tools_get_full_path_from_PATH(rec.dar_duc_path, tmp_string.c_str()) + " " + tmp_string2; if(p.aux_execute == "") p.aux_execute = tmp_string; else { if(rec.duc_and) p.aux_execute += string(" && "); else p.aux_execute += string(" ; "); p.aux_execute += tmp_string; } break; case '$': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.aux_pass == "") p.aux_pass = secu_string(optarg, strlen(optarg)); else rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); break; case '%': if(! tools_my_atoi(optarg, tmp)) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); else p.aux_crypto_size = (U_32)tmp; break; case '/': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.overwrite == nullptr) { try { p.overwrite = crit_action_create_from_string(*rec.dialog, crit_action_canonize_string(optarg), p.hourshift); } catch(Erange & e) { throw Erange(e.get_source(), string(gettext("Syntax error in overwriting policy: ") + e.get_message())); } } else rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); break; case '^': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); line_tools_slice_ownership(string(optarg), p.slice_perm, p.slice_user, p.slice_group); break; case '_': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); line_tools_repeat_param(string(optarg), p.repeat_count, p.repeat_byte); break; case '0': if(optarg == nullptr) p.sequential_read = true; else throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); break; case '1': if(p.sparse_file_min_size != sparse_file_min_size_default) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else { if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); try { p.sparse_file_min_size = tools_get_extended_size(optarg, rec.suffix_base); if(p.sparse_file_min_size == sparse_file_min_size_default) rec.dialog->message(tools_printf(gettext("%d is the default value for --sparse-file-min-size, no need to specify it on command line, ignoring"), sparse_file_min_size_default)); } catch(Edeci & e) { throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); } } break; case '2': if(p.dirty != dirtyb_warn) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else { if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(strcasecmp("ignore", optarg) == 0) p.dirty = dirtyb_ignore; else if(strcasecmp("no-warn", optarg) == 0) p.dirty = dirtyb_ok; else throw Erange("command_line.cpp:get_args_recursive", tools_printf(gettext("Unknown argument given to -2 : %s"), optarg)); } break; case '"': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); else { tlv_list tmp; argc_argv arg; bool ret; line_tools_read_from_pipe(rec.dialog, tools_str2int(optarg), tmp); line_tools_tlv_list2argv(*rec.dialog, tmp, arg); S_I optind_mem = line_tools_reset_getopt(); // save the external variable to use recursivity (see getopt) // reset getopt module ret = get_args_recursive(rec, p, arg.argc(), arg.argv()); skip_getopt(argc, argv, optind_mem); // restores getopt after recursion if(!ret) return false; } break; case 'q': p.quiet = true; break; case '.': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(p.user_comment != "") p.user_comment += " "; p.user_comment += optarg; break; case '3': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext("Missing argument to --hash"), char(lu))); if(!string_to_hash_algo(optarg, p.hash) || p.hash == hash_algo::none) throw Erange("get_args", string(gettext("Unknown parameter given to --hash option: ")) + optarg); break; case '9': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext("Missing argument to --min-digits"), char(lu))); else { try { line_tools_get_min_digits(optarg, p.num_digits, p.ref_num_digits, p.aux_num_digits); } catch(Erange & e) { throw Erange("get_args", string(gettext("Error while parsing --min-digits option: ")) + e.get_message()); } } break; case '=': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext("Missing argument to --backup-hook-execute"), char(lu))); line_tools_split_at_first_space(optarg, tmp_string, tmp_string2); tmp_string = line_tools_get_full_path_from_PATH(rec.dar_duc_path, tmp_string.c_str()) + " " + tmp_string2; if(p.backup_hook_execute == "") p.backup_hook_execute = tmp_string; else p.backup_hook_execute += string(" ; ") + tmp_string; break; case '>': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = false; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.backup_hook_include_exclude.push_back(tmp_pre_mask); break; case '<': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = true; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.backup_hook_include_exclude.push_back(tmp_pre_mask); break; case '4': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); p.scope = string_to_fsa(optarg); if(p.info_details) { string list; set::iterator it = p.scope.begin(); while(it != p.scope.end()) { list += " "; list += fsa_family_to_string(*it); ++it; } rec.dialog->message(string("FSA family in scope:") + list); } break; case '5': p.exclude_by_ea = true; if(optarg != nullptr) p.ea_name_for_exclusion = optarg; else p.ea_name_for_exclusion = ""; break; case '6': if(optarg != nullptr) p.delta_sig_min_size = tools_get_extended_size(optarg, rec.suffix_base); else throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); break; case '7': if(optarg != nullptr) { if(strlen(optarg) != 0) line_tools_split(optarg, ',', p.signatories); else throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); } else throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); break; case '8': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext("Missing argument to --delta"), char(lu))); if(strcasecmp(optarg, "sig") == 0) p.delta_sig = true; else if(strcasecmp(optarg, "no-patch") == 0) p.delta_diff = false; else if(strncasecmp(optarg,"sig:",4) == 0) { if(!p.delta_sig_len.equals_default()) rec.dialog->message(tools_printf(gettext(ONLY_ONCE), char(lu))); else { vector splitted; vector::iterator it; line_tools_split(optarg, ':', splitted); it = splitted.begin(); if(it == splitted.end()) throw SRC_BUG; ++it; // skipping the initial "sig" keyword // reading the function if(it == splitted.end()) throw Erange("get_args", tools_printf(gettext(INVALID_BS_FUNC), gettext("missing function name argument in string"))); p.delta_sig_len.fs_function = line_tools_string_to_sig_block_size_function(*it); ++it; // reading the multiplier if(it == splitted.end()) throw Erange("get_args", tools_printf(gettext(INVALID_BS_FUNC), gettext("missing multiplier argument in string"))); p.delta_sig_len.multiplier = tools_get_extended_size(*it, rec.suffix_base); ++it; // eventually reading the divisor if(it != splitted.end()) { p.delta_sig_len.divisor = tools_get_extended_size(*it, rec.suffix_base); ++it; } // eventually reading the min_block_len if(it != splitted.end()) { tmp_infinint = tools_get_extended_size(*it, rec.suffix_base); p.delta_sig_len.min_block_len = 0; tmp_infinint.unstack(p.delta_sig_len.min_block_len); if(!tmp_infinint.is_zero()) throw Erange("get_args", tools_printf(gettext(INVALID_BS_FUNC), gettext("too large value provided for the min block size"))); ++it; } // eventually reading the max_block_len if(it != splitted.end()) { tmp_infinint = tools_get_extended_size(*it, rec.suffix_base); p.delta_sig_len.max_block_len = 0; tmp_infinint.unstack(p.delta_sig_len.max_block_len); if(!tmp_infinint.is_zero()) throw Erange("get_args", tools_printf(gettext(INVALID_BS_FUNC), gettext("too large value provided for the min block size"))); ++it; } if(it != splitted.end()) throw Erange("get_args", tools_printf(gettext(INVALID_BS_FUNC), gettext("unexpected extra argument in string"))); p.delta_sig = true; } } else throw Erange("get_args", string(gettext("Unknown parameter given to --delta option: ")) + optarg); break; case '{': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = true; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.path_delta_include_exclude.push_back(tmp_pre_mask); break; case '}': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); tmp_pre_mask.file_listing = false; tmp_pre_mask.case_sensit = rec.case_sensit; tmp_pre_mask.included = false; tmp_pre_mask.mask = string(optarg); tmp_pre_mask.glob_exp = rec.glob_mode; rec.path_delta_include_exclude.push_back(tmp_pre_mask); break; case 'j': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(!tools_my_atoi(optarg, tmp)) throw Erange("get_args", tools_printf(gettext(INVALID_ARG), char(lu))); else { p.remote.network_retry = tmp; p.ref_remote.network_retry = tmp; p.aux_remote.network_retry = tmp; } break; case '\\': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); p.ignored_as_symlink = optarg; break; case '\'': if(optarg == nullptr) throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(lu))); if(strcasecmp(optarg, "any-inode-change") == 0) p.modet = modified_data_detection::any_inode_change; else if(strcasecmp(optarg, "mtime-and-size") == 0) p.modet = modified_data_detection::mtime_size; else throw Erange("get_args", string(gettext("Unknown parameter given to --modified-data-detection option: ")) + optarg); break; case ':': throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(optopt))); case '?': throw Erange("get_args", tools_printf(gettext("Unknown option -%c"),char(optopt))); default: throw Erange("get_args", tools_printf(gettext("Unknown option -%c"),char(lu))); } } return true; } static void usage(user_interaction & dialog, const char *command_name) { string name; shell_interaction *ptr = dynamic_cast(&dialog); line_tools_extract_basename(command_name, name); if(ptr != nullptr) ptr->change_non_interactive_output(cout); dialog.printf(gettext("usage: %s [ -c | -x | -d | -t | -l | -C | -+ ] [/] [options...]\n"), name.c_str()); dialog.printf(" %s -h\n", name.c_str()); dialog.printf(" %s -V\n", name.c_str()); dialog.printf(gettext("\n")); dialog.printf(gettext("Commands are:\n")); dialog.printf(gettext(" -c creates an archive\n")); dialog.printf(gettext(" -x extracts files from the archive\n")); dialog.printf(gettext(" -d compares the archive with the existing filesystem\n")); dialog.printf(gettext(" -t tests the archive integrity\n")); dialog.printf(gettext(" -l lists the contents of the archive\n")); dialog.printf(gettext(" -C isolates the catalogue from an archive\n")); dialog.printf(gettext(" -+ merge two archives / create a sub archive\n")); dialog.printf(gettext(" -y repair a truncated archive\n")); dialog.printf(gettext("\n")); dialog.printf(gettext(" -h displays this help information\n")); dialog.printf(gettext(" -V displays version information\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("Common options:\n")); dialog.printf(gettext(" -v[s|t|d|m|f|a] verbose output\n")); dialog.printf(gettext(" -q\t\t suppress final statistics report\n")); dialog.printf(gettext(" -vs\t\t display skipped files\n")); dialog.printf(gettext(" -R \t filesystem root directory (current dir by default)\n")); dialog.printf(gettext(" -X \t files to exclude from the operation (none by default)\n")); dialog.printf(gettext(" -I \t files to include in the operation (all by default)\n")); dialog.printf(gettext(" -P \t subdirectory to exclude from the operation\n")); dialog.printf(gettext(" -g \t subdirectory to include in the operation\n")); dialog.printf(gettext(" -[ filename contains a list of files to include\n")); dialog.printf(gettext(" -] \t filename contains a list of files to exclude\n")); dialog.printf(gettext(" -n\t\t don't overwrite files\n")); dialog.printf(gettext(" -w\t\t don't warn before overwriting files\n")); dialog.printf(gettext(" -wa\t\t don't warn before overwriting and removing files\n")); dialog.printf(gettext(" -b\t\t ring the terminal bell when user action is required\n")); dialog.printf(gettext(" -O[ignore-owner | mtime | inode-type] do not consider user and group\n")); dialog.printf(gettext("\t\t ownership\n")); dialog.printf(gettext(" -H [N]\t ignore shift in dates of an exact number of hours\n")); dialog.printf(gettext(" -E \t command to execute between slices\n")); dialog.printf(gettext(" -F \t same as -E but for the archive of reference\n")); dialog.printf(gettext(" -u \t mask to ignore certain EA\n")); dialog.printf(gettext(" -U \t mask to allow certain EA\n")); dialog.printf(gettext(" -K \t use as key to encrypt/decrypt\n")); dialog.printf(gettext(" -J \t same as -K but it does concern the archive of reference\n")); dialog.printf(gettext(" -# encryption block size\n")); dialog.printf(gettext(" -* same as -# but for archive of reference\n")); dialog.printf(gettext(" -B read options from given file\n")); dialog.printf(gettext(" -N\t\t do not read ~/.darrc nor /etc/darrc configuration file\n")); dialog.printf(gettext(" -e\t\t dry run, fake execution, nothing is produced\n")); dialog.printf(gettext(" -Q\t\t suppress the initial warning when not launched from a tty\n")); dialog.printf(gettext(" -aa\t\t do not try to preserve atime of file open for reading.\n")); dialog.printf(gettext(" -ac\t\t do not try to preserve ctime (default behavior).\n")); dialog.printf(gettext(" -am\t\t set ordered mode for all filters\n")); dialog.printf(gettext(" -an\t\t the masks that follow are now case insensitive\n")); dialog.printf(gettext(" -acase\t the masks that follow are now case sensitive\n")); dialog.printf(gettext(" -ar\t\t set the following masks to be regex expressions\n")); dialog.printf(gettext(" -ag\t\t set the following masks to be glob expressions\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("Saving/Isolation/merging/repairing options (to use with -c, -C, -+ or -y):\n")); dialog.printf(gettext(" -A [path/] archive to take as reference\n")); dialog.printf(gettext(" -@ [path/] auxiliary archive of reference for merging\n")); dialog.printf(gettext(" -$ \t encryption key for auxiliary archive\n")); dialog.printf(gettext(" -~ \t command between slices of the auxiliary archive\n")); dialog.printf(gettext(" -z [[algo:]level]\t compress data in archive. -z = -z9 = -zgzip:9\n")); dialog.printf(gettext(" Available algo: gzip,bzip2,lzo,xz. Exemples: -zlzo -zxz:5 -z1 -z\n")); dialog.printf(gettext(" -s split the archive in several files of size \n")); dialog.printf(gettext(" -S first file size (if different from following ones)\n")); dialog.printf(gettext(" -aSI \t slice size suffixes k, M, T, G, etc. are powers of 10\n")); dialog.printf(gettext(" -abinary\t slice size suffixes k, M, T, G, etc. are powers of 2\n")); dialog.printf(gettext(" -p\t\t pauses before writing to a new file\n")); dialog.printf(gettext(" -D\t\t excluded directories are stored as empty directories\n")); dialog.printf(gettext(" -Z \t do not compress the matching filenames\n")); dialog.printf(gettext(" -Y \t do only compress the matching filenames\n")); dialog.printf(gettext(" -m \t do not compress file smaller than \n")); dialog.printf(gettext(" --nodump\t do not backup, files having the nodump 'd' flag set\n")); dialog.printf(gettext(" -@ [path/] Do on-fly catalogue isolation of the resulting archive\n")); dialog.printf(gettext(" -M\t\t stay in the same filesystem while scanning directories\n")); dialog.printf(gettext(" -,\t\t ignore directories that follow the Directory Tagging\n")); dialog.printf(gettext("\t\t Standard\n")); dialog.printf(gettext(" -/ \t which way dar can overwrite files at archive merging or\n")); dialog.printf(gettext("\t\t extraction time\n")); dialog.printf(gettext(" -^ \t permission[:user[:group]] of created slices\n")); dialog.printf(gettext(" -8 sig\t add delta signature to perform binary delta if used as ref.")); dialog.printf(gettext("\n")); dialog.printf(gettext("Restoring options (to use with -x) :\n")); dialog.printf(gettext(" -k\t\t do not remove files destroyed since the reference backup\n")); dialog.printf(gettext(" -r\t\t do not restore file older than those on filesystem\n")); dialog.printf(gettext(" -f\t\t do not restore directory structure\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("Reading options (to use with -x, -d, -t, -l, -A)\n")); dialog.printf(gettext(" -i pipe to use instead of std input to read data from dar_slave\n")); dialog.printf(gettext(" -o pipe to use instead of std output to orders dar_slave\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("Listing options (to use with -l):\n")); dialog.printf(gettext(" -T\t\t tree output format\n")); dialog.printf(gettext(" -as\t\t only list files saved in the archive\n")); dialog.printf(gettext("\n\n")); dialog.printf(gettext("Type \"man dar\" for more details and for all other available options.\n")); } static void show_warranty(user_interaction & dialog) { shell_interaction *ptr = dynamic_cast(&dialog); if(ptr != nullptr) ptr->change_non_interactive_output(cout); dialog.printf(" NO WARRANTY\n"); dialog.printf("\n"); dialog.printf(" 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n"); dialog.printf("FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n"); dialog.printf("OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n"); dialog.printf("PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n"); dialog.printf("OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n"); dialog.printf("MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\n"); dialog.printf("TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\n"); dialog.printf("PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n"); dialog.printf("REPAIR OR CORRECTION.\n"); dialog.printf("\n"); dialog.printf(" 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n"); dialog.printf("WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n"); dialog.printf("REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n"); dialog.printf("INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n"); dialog.printf("OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n"); dialog.printf("TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n"); dialog.printf("YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n"); dialog.printf("PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n"); dialog.printf("POSSIBILITY OF SUCH DAMAGES.\n"); dialog.printf("\n"); } static void show_license(user_interaction & dialog) { shell_interaction *ptr = dynamic_cast(&dialog); if(ptr != nullptr) ptr->change_non_interactive_output(cout); dialog.printf(" GNU GENERAL PUBLIC LICENSE\n"); dialog.printf(" Version 2, June 1991\n"); dialog.printf("\n"); dialog.printf(" Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n"); dialog.printf(" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n"); dialog.printf(" Everyone is permitted to copy and distribute verbatim copies\n"); dialog.printf(" of this license document, but changing it is not allowed.\n"); dialog.printf("\n"); dialog.printf(" Preamble\n"); dialog.printf("\n"); dialog.printf(" The licenses for most software are designed to take away your\n"); dialog.printf("freedom to share and change it. By contrast, the GNU General Public\n"); dialog.printf("License is intended to guarantee your freedom to share and change free\n"); dialog.printf("software--to make sure the software is free for all its users. This\n"); dialog.printf("General Public License applies to most of the Free Software\n"); dialog.printf("Foundation's software and to any other program whose authors commit to\n"); dialog.printf("using it. (Some other Free Software Foundation software is covered by\n"); dialog.printf("the GNU Library General Public License instead.) You can apply it to\n"); dialog.printf("your programs, too.\n"); dialog.printf("\n"); dialog.printf(" When we speak of free software, we are referring to freedom, not\n"); dialog.printf("price. Our General Public Licenses are designed to make sure that you\n"); dialog.printf("have the freedom to distribute copies of free software (and charge for\n"); dialog.printf("this service if you wish), that you receive source code or can get it\n"); dialog.printf("if you want it, that you can change the software or use pieces of it\n"); dialog.printf("in new free programs; and that you know you can do these things.\n"); dialog.printf("\n"); dialog.printf(" To protect your rights, we need to make restrictions that forbid\n"); dialog.printf("anyone to deny you these rights or to ask you to surrender the rights.\n"); dialog.printf("These restrictions translate to certain responsibilities for you if you\n"); dialog.printf("distribute copies of the software, or if you modify it.\n"); dialog.printf("\n"); dialog.printf(" For example, if you distribute copies of such a program, whether\n"); dialog.printf("gratis or for a fee, you must give the recipients all the rights that\n"); dialog.printf("you have. You must make sure that they, too, receive or can get the\n"); dialog.printf("source code. And you must show them these terms so they know their\n"); dialog.printf("rights.\n"); dialog.printf("\n"); dialog.printf(" We protect your rights with two steps: (1) copyright the software, and\n"); dialog.printf("(2) offer you this license which gives you legal permission to copy,\n"); dialog.printf("distribute and/or modify the software.\n"); dialog.printf("\n"); dialog.printf(" Also, for each author's protection and ours, we want to make certain\n"); dialog.printf("that everyone understands that there is no warranty for this free\n"); dialog.printf("software. If the software is modified by someone else and passed on, we\n"); dialog.printf("want its recipients to know that what they have is not the original, so\n"); dialog.printf("that any problems introduced by others will not reflect on the original\n"); dialog.printf("authors' reputations.\n"); dialog.printf("\n"); dialog.printf(" Finally, any free program is threatened constantly by software\n"); dialog.printf("patents. We wish to avoid the danger that redistributors of a free\n"); dialog.printf("program will individually obtain patent licenses, in effect making the\n"); dialog.printf("program proprietary. To prevent this, we have made it clear that any\n"); dialog.printf("patent must be licensed for everyone's free use or not licensed at all.\n"); dialog.printf("\n"); dialog.printf(" The precise terms and conditions for copying, distribution and\n"); dialog.printf("modification follow.\n"); dialog.printf(" \n"); dialog.printf(" GNU GENERAL PUBLIC LICENSE\n"); dialog.printf(" TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"); dialog.printf("\n"); dialog.printf(" 0. This License applies to any program or other work which contains\n"); dialog.printf("a notice placed by the copyright holder saying it may be distributed\n"); dialog.printf("under the terms of this General Public License. The \"Program\", below,\n"); dialog.printf("refers to any such program or work, and a \"work based on the Program\"\n"); dialog.printf("means either the Program or any derivative work under copyright law:\n"); dialog.printf("that is to say, a work containing the Program or a portion of it,\n"); dialog.printf("either verbatim or with modifications and/or translated into another\n"); dialog.printf("language. (Hereinafter, translation is included without limitation in\n"); dialog.printf("the term \"modification\".) Each licensee is addressed as \"you\".\n"); dialog.printf("\n"); dialog.printf("Activities other than copying, distribution and modification are not\n"); dialog.printf("covered by this License; they are outside its scope. The act of\n"); dialog.printf("running the Program is not restricted, and the output from the Program\n"); dialog.printf("is covered only if its contents constitute a work based on the\n"); dialog.printf("Program (independent of having been made by running the Program).\n"); dialog.printf("Whether that is true depends on what the Program does.\n"); dialog.printf("\n"); dialog.printf(" 1. You may copy and distribute verbatim copies of the Program's\n"); dialog.printf("source code as you receive it, in any medium, provided that you\n"); dialog.printf("conspicuously and appropriately publish on each copy an appropriate\n"); dialog.printf("copyright notice and disclaimer of warranty; keep intact all the\n"); dialog.printf("notices that refer to this License and to the absence of any warranty;\n"); dialog.printf("and give any other recipients of the Program a copy of this License\n"); dialog.printf("along with the Program.\n"); dialog.printf("\n"); dialog.printf("You may charge a fee for the physical act of transferring a copy, and\n"); dialog.printf("you may at your option offer warranty protection in exchange for a fee.\n"); dialog.printf("\n"); dialog.printf(" 2. You may modify your copy or copies of the Program or any portion\n"); dialog.printf("of it, thus forming a work based on the Program, and copy and\n"); dialog.printf("distribute such modifications or work under the terms of Section 1\n"); dialog.printf("above, provided that you also meet all of these conditions:\n"); dialog.printf("\n"); dialog.printf(" a) You must cause the modified files to carry prominent notices\n"); dialog.printf(" stating that you changed the files and the date of any change.\n"); dialog.printf("\n"); dialog.printf(" b) You must cause any work that you distribute or publish, that in\n"); dialog.printf(" whole or in part contains or is derived from the Program or any\n"); dialog.printf(" part thereof, to be licensed as a whole at no charge to all third\n"); dialog.printf(" parties under the terms of this License.\n"); dialog.printf("\n"); dialog.printf(" c) If the modified program normally reads commands interactively\n"); dialog.printf(" when run, you must cause it, when started running for such\n"); dialog.printf(" interactive use in the most ordinary way, to print or display an\n"); dialog.printf(" announcement including an appropriate copyright notice and a\n"); dialog.printf(" notice that there is no warranty (or else, saying that you provide\n"); dialog.printf(" a warranty) and that users may redistribute the program under\n"); dialog.printf(" these conditions, and telling the user how to view a copy of this\n"); dialog.printf(" License. (Exception: if the Program itself is interactive but\n"); dialog.printf(" does not normally print such an announcement, your work based on\n"); dialog.printf(" the Program is not required to print an announcement.)\n"); dialog.printf(" \n"); dialog.printf("These requirements apply to the modified work as a whole. If\n"); dialog.printf("identifiable sections of that work are not derived from the Program,\n"); dialog.printf("and can be reasonably considered independent and separate works in\n"); dialog.printf("themselves, then this License, and its terms, do not apply to those\n"); dialog.printf("sections when you distribute them as separate works. But when you\n"); dialog.printf("distribute the same sections as part of a whole which is a work based\n"); dialog.printf("on the Program, the distribution of the whole must be on the terms of\n"); dialog.printf("this License, whose permissions for other licensees extend to the\n"); dialog.printf("entire whole, and thus to each and every part regardless of who wrote it.\n"); dialog.printf("\n"); dialog.printf("Thus, it is not the intent of this section to claim rights or contest\n"); dialog.printf("your rights to work written entirely by you; rather, the intent is to\n"); dialog.printf("exercise the right to control the distribution of derivative or\n"); dialog.printf("collective works based on the Program.\n"); dialog.printf("\n"); dialog.printf("In addition, mere aggregation of another work not based on the Program\n"); dialog.printf("with the Program (or with a work based on the Program) on a volume of\n"); dialog.printf("a storage or distribution medium does not bring the other work under\n"); dialog.printf("the scope of this License.\n"); dialog.printf("\n"); dialog.printf(" 3. You may copy and distribute the Program (or a work based on it,\n"); dialog.printf("under Section 2) in object code or executable form under the terms of\n"); dialog.printf("Sections 1 and 2 above provided that you also do one of the following:\n"); dialog.printf("\n"); dialog.printf(" a) Accompany it with the complete corresponding machine-readable\n"); dialog.printf(" source code, which must be distributed under the terms of Sections\n"); dialog.printf(" 1 and 2 above on a medium customarily used for software interchange; or,\n"); dialog.printf("\n"); dialog.printf(" b) Accompany it with a written offer, valid for at least three\n"); dialog.printf(" years, to give any third party, for a charge no more than your\n"); dialog.printf(" cost of physically performing source distribution, a complete\n"); dialog.printf(" machine-readable copy of the corresponding source code, to be\n"); dialog.printf(" distributed under the terms of Sections 1 and 2 above on a medium\n"); dialog.printf(" customarily used for software interchange; or,\n"); dialog.printf("\n"); dialog.printf(" c) Accompany it with the information you received as to the offer\n"); dialog.printf(" to distribute corresponding source code. (This alternative is\n"); dialog.printf(" allowed only for noncommercial distribution and only if you\n"); dialog.printf(" received the program in object code or executable form with such\n"); dialog.printf(" an offer, in accord with Subsection b above.)\n"); dialog.printf("\n"); dialog.printf("The source code for a work means the preferred form of the work for\n"); dialog.printf("making modifications to it. For an executable work, complete source\n"); dialog.printf("code means all the source code for all modules it contains, plus any\n"); dialog.printf("associated interface definition files, plus the scripts used to\n"); dialog.printf("control compilation and installation of the executable. However, as a\n"); dialog.printf("special exception, the source code distributed need not include\n"); dialog.printf("anything that is normally distributed (in either source or binary\n"); dialog.printf("form) with the major components (compiler, kernel, and so on) of the\n"); dialog.printf("operating system on which the executable runs, unless that component\n"); dialog.printf("itself accompanies the executable.\n"); dialog.printf("\n"); dialog.printf("If distribution of executable or object code is made by offering\n"); dialog.printf("access to copy from a designated place, then offering equivalent\n"); dialog.printf("access to copy the source code from the same place counts as\n"); dialog.printf("distribution of the source code, even though third parties are not\n"); dialog.printf("compelled to copy the source along with the object code.\n"); dialog.printf(" \n"); dialog.printf(" 4. You may not copy, modify, sublicense, or distribute the Program\n"); dialog.printf("except as expressly provided under this License. Any attempt\n"); dialog.printf("otherwise to copy, modify, sublicense or distribute the Program is\n"); dialog.printf("void, and will automatically terminate your rights under this License.\n"); dialog.printf("However, parties who have received copies, or rights, from you under\n"); dialog.printf("this License will not have their licenses terminated so long as such\n"); dialog.printf("parties remain in full compliance.\n"); dialog.printf("\n"); dialog.printf(" 5. You are not required to accept this License, since you have not\n"); dialog.printf("signed it. However, nothing else grants you permission to modify or\n"); dialog.printf("distribute the Program or its derivative works. These actions are\n"); dialog.printf("prohibited by law if you do not accept this License. Therefore, by\n"); dialog.printf("modifying or distributing the Program (or any work based on the\n"); dialog.printf("Program), you indicate your acceptance of this License to do so, and\n"); dialog.printf("all its terms and conditions for copying, distributing or modifying\n"); dialog.printf("the Program or works based on it.\n"); dialog.printf("\n"); dialog.printf(" 6. Each time you redistribute the Program (or any work based on the\n"); dialog.printf("Program), the recipient automatically receives a license from the\n"); dialog.printf("original licensor to copy, distribute or modify the Program subject to\n"); dialog.printf("these terms and conditions. You may not impose any further\n"); dialog.printf("restrictions on the recipients' exercise of the rights granted herein.\n"); dialog.printf("You are not responsible for enforcing compliance by third parties to\n"); dialog.printf("this License.\n"); dialog.printf("\n"); dialog.printf(" 7. If, as a consequence of a court judgment or allegation of patent\n"); dialog.printf("infringement or for any other reason (not limited to patent issues),\n"); dialog.printf("conditions are imposed on you (whether by court order, agreement or\n"); dialog.printf("otherwise) that contradict the conditions of this License, they do not\n"); dialog.printf("excuse you from the conditions of this License. If you cannot\n"); dialog.printf("distribute so as to satisfy simultaneously your obligations under this\n"); dialog.printf("License and any other pertinent obligations, then as a consequence you\n"); dialog.printf("may not distribute the Program at all. For example, if a patent\n"); dialog.printf("license would not permit royalty-free redistribution of the Program by\n"); dialog.printf("all those who receive copies directly or indirectly through you, then\n"); dialog.printf("the only way you could satisfy both it and this License would be to\n"); dialog.printf("refrain entirely from distribution of the Program.\n"); dialog.printf("\n"); dialog.printf("If any portion of this section is held invalid or unenforceable under\n"); dialog.printf("any particular circumstance, the balance of the section is intended to\n"); dialog.printf("apply and the section as a whole is intended to apply in other\n"); dialog.printf("circumstances.\n"); dialog.printf("\n"); dialog.printf("It is not the purpose of this section to induce you to infringe any\n"); dialog.printf("patents or other property right claims or to contest validity of any\n"); dialog.printf("such claims; this section has the sole purpose of protecting the\n"); dialog.printf("integrity of the free software distribution system, which is\n"); dialog.printf("implemented by public license practices. Many people have made\n"); dialog.printf("generous contributions to the wide range of software distributed\n"); dialog.printf("through that system in reliance on consistent application of that\n"); dialog.printf("system; it is up to the author/donor to decide if he or she is willing\n"); dialog.printf("to distribute software through any other system and a licensee cannot\n"); dialog.printf("impose that choice.\n"); dialog.printf("\n"); dialog.printf("This section is intended to make thoroughly clear what is believed to\n"); dialog.printf("be a consequence of the rest of this License.\n"); dialog.printf(" \n"); dialog.printf(" 8. If the distribution and/or use of the Program is restricted in\n"); dialog.printf("certain countries either by patents or by copyrighted interfaces, the\n"); dialog.printf("original copyright holder who places the Program under this License\n"); dialog.printf("may add an explicit geographical distribution limitation excluding\n"); dialog.printf("those countries, so that distribution is permitted only in or among\n"); dialog.printf("countries not thus excluded. In such case, this License incorporates\n"); dialog.printf("the limitation as if written in the body of this License.\n"); dialog.printf("\n"); dialog.printf(" 9. The Free Software Foundation may publish revised and/or new versions\n"); dialog.printf("of the General Public License from time to time. Such new versions will\n"); dialog.printf("be similar in spirit to the present version, but may differ in detail to\n"); dialog.printf("address new problems or concerns.\n"); dialog.printf("\n"); dialog.printf("Each version is given a distinguishing version number. If the Program\n"); dialog.printf("specifies a version number of this License which applies to it and \"any\n"); dialog.printf("later version\", you have the option of following the terms and conditions\n"); dialog.printf("either of that version or of any later version published by the Free\n"); dialog.printf("Software Foundation. If the Program does not specify a version number of\n"); dialog.printf("this License, you may choose any version ever published by the Free Software\n"); dialog.printf("Foundation.\n"); dialog.printf("\n"); dialog.printf(" 10. If you wish to incorporate parts of the Program into other free\n"); dialog.printf("programs whose distribution conditions are different, write to the author\n"); dialog.printf("to ask for permission. For software which is copyrighted by the Free\n"); dialog.printf("Software Foundation, write to the Free Software Foundation; we sometimes\n"); dialog.printf("make exceptions for this. Our decision will be guided by the two goals\n"); dialog.printf("of preserving the free status of all derivatives of our free software and\n"); dialog.printf("of promoting the sharing and reuse of software generally.\n"); dialog.printf("\n"); show_warranty(dialog); dialog.printf(" END OF TERMS AND CONDITIONS\n"); dialog.printf(" \n"); dialog.printf(" How to Apply These Terms to Your New Programs\n"); dialog.printf("\n"); dialog.printf(" If you develop a new program, and you want it to be of the greatest\n"); dialog.printf("possible use to the public, the best way to achieve this is to make it\n"); dialog.printf("free software which everyone can redistribute and change under these terms.\n"); dialog.printf("\n"); dialog.printf(" To do so, attach the following notices to the program. It is safest\n"); dialog.printf("to attach them to the start of each source file to most effectively\n"); dialog.printf("convey the exclusion of warranty; and each file should have at least\n"); dialog.printf("the \"copyright\" line and a pointer to where the full notice is found.\n"); dialog.printf("\n"); dialog.printf(" \n"); dialog.printf(" Copyright (C) \n"); dialog.printf("\n"); dialog.printf(" This program is free software; you can redistribute it and/or modify\n"); dialog.printf(" it under the terms of the GNU General Public License as published by\n"); dialog.printf(" the Free Software Foundation; either version 2 of the License, or\n"); dialog.printf(" (at your option) any later version.\n"); dialog.printf("\n"); dialog.printf(" This program is distributed in the hope that it will be useful,\n"); dialog.printf(" but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); dialog.printf(" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); dialog.printf(" GNU General Public License for more details.\n"); dialog.printf("\n"); dialog.printf(" You should have received a copy of the GNU General Public License\n"); dialog.printf(" along with this program; if not, write to the Free Software\n"); dialog.printf(" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n"); dialog.printf("\n"); dialog.printf("\n"); dialog.printf("Also add information on how to contact you by electronic and paper mail.\n"); dialog.printf("\n"); dialog.printf("If the program is interactive, make it output a short notice like this\n"); dialog.printf("when it starts in an interactive mode:\n"); dialog.printf("\n"); dialog.printf(" Gnomovision version 69, Copyright (C) year name of author\n"); dialog.printf(" Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n"); dialog.printf(" This is free software, and you are welcome to redistribute it\n"); dialog.printf(" under certain conditions; type `show c' for details.\n"); dialog.printf("\n"); dialog.printf("The hypothetical commands `show w' and `show c' should show the appropriate\n"); dialog.printf("parts of the General Public License. Of course, the commands you use may\n"); dialog.printf("be called something other than `show w' and `show c'; they could even be\n"); dialog.printf("mouse-clicks or menu items--whatever suits your program.\n"); dialog.printf("\n"); dialog.printf("You should also get your employer (if you work as a programmer) or your\n"); dialog.printf("school, if any, to sign a \"copyright disclaimer\" for the program, if\n"); dialog.printf("necessary. Here is a sample; alter the names:\n"); dialog.printf("\n"); dialog.printf(" Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n"); dialog.printf(" `Gnomovision' (which makes passes at compilers) written by James Hacker.\n"); dialog.printf("\n"); dialog.printf(" , 1 April 1989\n"); dialog.printf(" Ty Coon, President of Vice\n"); dialog.printf("\n"); dialog.printf("This General Public License does not permit incorporating your program into\n"); dialog.printf("proprietary programs. If your program is a subroutine library, you may\n"); dialog.printf("consider it more useful to permit linking proprietary applications with the\n"); dialog.printf("library. If this is what you want to do, use the GNU Library General\n"); dialog.printf("Public License instead of this License.\n"); dialog.printf("\n"); } static void show_version(user_interaction & dialog, const char *command_name) { string name; line_tools_extract_basename(command_name, name); U_I maj, med, min; shell_interaction *ptr = dynamic_cast(&dialog); get_version(maj, med, min); if(ptr != nullptr) ptr->change_non_interactive_output(cout); dialog.message(tools_printf("\n %s version %s, Copyright (C) 2002-2024 Denis Corbin\n", name.c_str(), ::dar_version()) + " " + dar_suite_command_line_features() + "\n" + (maj > 2 ? tools_printf(gettext(" Using libdar %u.%u.%u built with compilation time options:"), maj, med, min) : tools_printf(gettext(" Using libdar %u.%u built with compilation time options:"), maj, min))); line_tools_display_features(dialog); dialog.printf("\n"); dialog.message(tools_printf(gettext(" compiled the %s with %s version %s\n"), __DATE__, CC_NAT, __VERSION__) + tools_printf(gettext(" %s is part of the Disk ARchive suite (Release %s)\n"), name.c_str(), PACKAGE_VERSION) + tools_printf(gettext(" %s comes with ABSOLUTELY NO WARRANTY; for details\n type `%s -W'."), name.c_str(), name.c_str()) + tools_printf(gettext(" This is free software, and you are welcome\n to redistribute it under certain conditions;")) + tools_printf(gettext(" type `%s -L | more'\n for details.\n\n"), name.c_str())); } #if HAVE_GETOPT_LONG const struct option *get_long_opt() { static const struct option ret[] = { {"beep", no_argument, nullptr, 'b' }, {"create", required_argument, nullptr, 'c'}, {"diff", required_argument, nullptr, 'd'}, {"help", no_argument, nullptr, 'h'}, {"input", required_argument, nullptr, 'i'}, {"deleted", optional_argument, nullptr, 'k'}, {"no-delete", no_argument, nullptr, 'k'}, // backward compatiblity {"list", required_argument, nullptr, 'l'}, {"no-overwrite", no_argument, nullptr, 'n'}, {"output", required_argument, nullptr, 'o'}, {"pause", optional_argument, nullptr, 'p'}, {"recent", no_argument, nullptr, 'r'}, {"slice", required_argument, nullptr, 's'}, {"test", required_argument, nullptr, 't'}, {"exclude-ea", required_argument, nullptr, 'u'}, {"verbose", optional_argument, nullptr, 'v'}, {"no-warn", optional_argument, nullptr, 'w'}, {"extract", required_argument, nullptr, 'x'}, {"compression", required_argument, nullptr, 'z'}, {"ref", required_argument, nullptr, 'A'}, {"isolate", required_argument, nullptr, 'C'}, {"empty-dir", no_argument, nullptr, 'D'}, {"include", required_argument, nullptr, 'I'}, {"prune", required_argument, nullptr, 'P'}, {"fs-root", required_argument, nullptr, 'R'}, {"first-slice", required_argument, nullptr, 'S'}, {"include-ea", required_argument, nullptr, 'U'}, {"version", no_argument, nullptr, 'V'}, {"exclude", required_argument, nullptr, 'X'}, {"ignore-owner", no_argument, nullptr, 'O'}, {"comparison-field", optional_argument, nullptr, 'O'}, {"tree-format", no_argument, nullptr, 'T'}, {"list-format", required_argument, nullptr, 'T'}, {"execute", required_argument, nullptr, 'E'}, {"execute-ref",required_argument, nullptr, 'F'}, {"ref-execute",required_argument, nullptr, 'F'}, {"key", required_argument, nullptr, 'K'}, {"key-ref", required_argument, nullptr, 'J'}, {"ref-key", required_argument, nullptr, 'J'}, {"include-compression", required_argument, nullptr, 'Y'}, {"exclude-compression", required_argument, nullptr, 'Z'}, {"batch", required_argument, nullptr, 'B'}, {"flat", no_argument, nullptr, 'f'}, {"mincompr", required_argument, nullptr, 'm'}, {"noconf", no_argument, nullptr, 'N'}, {"nodump", no_argument, nullptr, ' '}, {"hour", optional_argument, nullptr, 'H'}, {"alter", optional_argument, nullptr, 'a'}, {"empty", no_argument, nullptr, 'e'}, {"dry-run", no_argument, nullptr, 'e'}, {"on-fly-isolate", required_argument, nullptr, '@'}, {"no-mount-points", optional_argument, nullptr, 'M'}, {"mount-points", optional_argument, nullptr, 'M'}, {"go-into", required_argument, nullptr, 'g'}, {"crypto-block", required_argument, nullptr, '#'}, {"ref-crypto-block", required_argument, nullptr, '*'}, {"crypto-block-ref", required_argument, nullptr, '*'}, {"cache-directory-tagging", no_argument, nullptr, ','}, {"include-from-file", required_argument, nullptr, '['}, {"exclude-from-file", required_argument, nullptr, ']'}, {"merge", required_argument, nullptr, '+'}, {"aux", required_argument, nullptr, '@'}, {"aux-ref", required_argument, nullptr, '@'}, {"aux-key", required_argument, nullptr, '$'}, {"aux-execute", required_argument, nullptr, '~'}, {"aux-crypto-block", required_argument, nullptr, '%'}, {"quiet", no_argument, nullptr, 'q'}, {"overwriting-policy", required_argument, nullptr, '/' }, {"slice-mode", required_argument, nullptr, '^' }, {"retry-on-change", required_argument, nullptr, '_' }, {"pipe-fd", required_argument, nullptr, '"' }, {"sequential-read", no_argument, nullptr, '0'}, {"sparse-file-min-size", required_argument, nullptr, '1'}, {"dirty-behavior", required_argument, nullptr, '2'}, {"user-comment", required_argument, nullptr, '.'}, {"hash", required_argument, nullptr, '3'}, {"min-digits", required_argument, nullptr, '9'}, {"backup-hook-include", required_argument, nullptr, '<'}, {"backup-hook-exclude", required_argument, nullptr, '>'}, {"backup-hook-execute", required_argument, nullptr, '='}, {"fsa-scope", required_argument, nullptr, '4'}, {"exclude-by-ea", optional_argument, nullptr, '5'}, {"sign", required_argument, nullptr, '7'}, {"multi-thread", required_argument, nullptr, 'G'}, {"delta", required_argument, nullptr, '8'}, {"include-delta-sig", required_argument, nullptr, '{'}, {"exclude-delta-sig", required_argument, nullptr, '}'}, {"delta-sig-min-size", required_argument, nullptr, '6'}, {"network-retry-delay", required_argument, nullptr, 'j'}, {"ignored-as-symlink", required_argument, nullptr, '\\'}, {"add-missing-catalogue", required_argument, nullptr, 'y'}, {"modified-data-detection", required_argument, nullptr, '\''}, {"kdf-param", required_argument, nullptr, 'T'}, { nullptr, 0, nullptr, 0 } }; return ret; } #endif static void make_args_from_file(shared_ptr & dialog, operation op, const deque & targets, const string & filename, S_I & argc, char **&argv, deque & read_targets, bool info_details) { deque cibles; deque locally_unread_targets; argv = nullptr; argc = 0; fichier_local conf = fichier_local(filename, false); // the object conf will close fd //////// // removing the comments from file // no_comment sousconf = no_comment(conf); //////// // defining the conditional syntax targets // that will be considered in the file cibles = targets; cibles.push_back("all"); switch(op) { case noop: cibles.push_back("default"); break; case create: cibles.push_back("create"); break; case extract: cibles.push_back("extract"); break; case diff: cibles.push_back("diff"); break; case test: cibles.push_back("test"); break; case listing: cibles.push_back("listing"); cibles.push_back("list"); break; case merging: cibles.push_back("merge"); break; case isolate: cibles.push_back("isolate"); break; case repairing: cibles.push_back("repair"); break; default: throw SRC_BUG; } ////// // hide the targets we don't want to see // config_file surconf = config_file(cibles, sousconf); ////// // now we have surconf -> sousconf -> conf -> fd // which makes the job to remove comments // and hide unwanted conditional statements on the fly // surconf can be used as a normal file. // const char *command = "dar"; char *pseudo_command = nullptr; try { deque mots; // now parsing the file and cutting words // taking care of quotes // line_tools_split_in_words(surconf, mots); // now converting the mots of type deque to argc/argv arguments // argc = mots.size()+1; if(argc < 0) throw SRC_BUG; // integer overflow occurred argv = new (nothrow) char *[argc]; if(argv == nullptr) throw Ememory("make_args_from_file"); for(S_I i = 0; i < argc; ++i) argv[i] = nullptr; // adding a fake "dar" word as first argument (= argv[0]) // char *pseudo_command = new (nothrow) char[strlen(command)+1]; if(pseudo_command == nullptr) throw Ememory("make_args_from_file"); strncpy(pseudo_command, command, strlen(command)); pseudo_command[strlen(command)] = '\0'; argv[0] = pseudo_command; pseudo_command = nullptr; if(info_details) dialog->printf(gettext("Arguments read from %S :"), &filename); for(U_I i = 0; i < mots.size(); ++i) { argv[i+1] = tools_str2charptr(mots[i]); // mots[i] goes to argv[i+1] ! if(info_details) dialog->printf(" \"%s\"", argv[i+1]); } if(info_details) dialog->printf("\n"); } catch(...) { if(argv != nullptr) { for(S_I i = 0; i < argc; ++i) if(argv[i] != nullptr) delete[] argv[i]; delete[] argv; argv = nullptr; } argc = 0; if(pseudo_command != nullptr) { delete[] pseudo_command; pseudo_command = nullptr; } throw; } line_tools_merge_to_deque(read_targets, surconf.get_read_targets()); } static void destroy(S_I argc, char **argv) { S_I i = 0; for(i = 0; i < argc; ++i) delete [] argv[i]; delete [] argv; } static void skip_getopt(S_I argc, char * const argv[], S_I next_to_read) { (void)line_tools_reset_getopt(); #if HAVE_GETOPT_LONG while(getopt_long(argc, argv, OPT_STRING, get_long_opt(), nullptr) != EOF && optind < next_to_read) ; #else while(getopt(argc, argv, OPT_STRING) != EOF && optind < next_to_read) ; #endif } static bool update_with_config_files(recursive_param & rec, line_param & p) { string buffer; enum { syntax, ok, unknown } retour = unknown; S_I rec_c = 0; char **rec_v = nullptr; (void)line_tools_reset_getopt(); // trying to open $HOME/.darrc buffer = string(rec.home) + "/.darrc"; try { make_args_from_file(rec.dialog, p.op, rec.non_options, buffer, rec_c, rec_v, rec.read_targets, p.info_details); try { try { if(! get_args_recursive(rec, p, rec_c, rec_v)) retour = syntax; else retour = ok; } catch(Erange & e) { Erange more = Erange(e.get_source(), tools_printf(gettext("In included file %S: "), &buffer) + e.get_message()); throw more; } } catch(...) { if(rec_v != nullptr) { destroy(rec_c, rec_v); rec_v = nullptr; rec_c = 0; } throw; } if(rec_v != nullptr) { destroy(rec_c, rec_v); rec_v = nullptr; rec_c = 0; } } catch(Esystem & e) { switch(e.get_code()) { case Esystem::io_exist: throw SRC_BUG; case Esystem::io_absent: // failed openning the file, // nothing to do, // we will try the other config file // below break; case Esystem::io_access: e.prepend_message(tools_printf(gettext("Failed reading %S: "), &buffer)); throw; case Esystem::io_ro_fs: throw SRC_BUG; // should not succeed as we open in read mode default: throw SRC_BUG; } } catch(Erange & e) { if(e.get_source() != "make_args_from_file") throw; } rec_c = 0; rec_v = nullptr; if(retour == unknown) { // trying to open DAR_SYS_DIR/darrc buffer = string(DAR_SYS_DIR) + "/darrc"; try { make_args_from_file(rec.dialog, p.op, rec.non_options, buffer, rec_c, rec_v, rec.read_targets, p.info_details); try { (void)line_tools_reset_getopt(); // reset getopt call try { if(! get_args_recursive(rec, p, rec_c, rec_v)) retour = syntax; else retour = ok; } catch(Erange & e) { Erange more = Erange(e.get_source(), tools_printf(gettext("In included file %S: "), &buffer) + e.get_message()); throw more; } } catch(...) { if(rec_v != nullptr) { destroy(rec_c, rec_v); rec_v = nullptr; rec_c = 0; } throw; } if(rec_v != nullptr) { destroy(rec_c, rec_v); rec_v = nullptr; rec_c = 0; } } catch(Esystem & e) { switch(e.get_code()) { case Esystem::io_exist: throw SRC_BUG; case Esystem::io_absent: // failed openning the file, // nothing to do, break; case Esystem::io_access: rec.dialog->printf(gettext("Warning: Failed reading %S: "), &buffer); throw; case Esystem::io_ro_fs: throw SRC_BUG; // should not succeed as we open in read mode default: throw SRC_BUG; } } catch(Erange & e) { if(e.get_source() != "make_args_from_file") throw; } } return retour != syntax; } static mask *make_include_exclude_name(const string & x, mask_opt opt) { mask *ret = nullptr; if(opt.glob_exp) ret = new (nothrow) simple_mask(x, opt.case_sensit); else ret = new (nothrow) regular_mask(x, opt.case_sensit); if(ret == nullptr) throw Ememory("make_include_exclude_name"); else return ret; } static mask *make_exclude_path_ordered(const string & x, mask_opt opt) { mask *ret = nullptr; if(opt.file_listing) { ret = new (nothrow) mask_list(x, opt.case_sensit, opt.prefix, false); if(ret == nullptr) throw Ememory("make_exclude_path"); } else // not file listing mask { if(opt.glob_exp) { ou_mask *val = new (nothrow) ou_mask(); if(val == nullptr) throw Ememory("make_exclude_path"); val->add_mask(simple_mask((opt.prefix + x).display(), opt.case_sensit)); val->add_mask(simple_mask((opt.prefix + x).display() + "/*", opt.case_sensit)); ret = val; } else // regex { ret = new (nothrow) regular_mask(line_tools_build_regex_for_exclude_mask(opt.prefix.display(), x), opt.case_sensit); if(ret == nullptr) throw Ememory("make_exclude_path"); } } return ret; } static mask *make_exclude_path_unordered(const string & x, mask_opt opt) { mask *ret = nullptr; if(opt.file_listing) ret = new (nothrow) mask_list(x, opt.case_sensit, opt.prefix, false); else if(opt.glob_exp) ret = new (nothrow) simple_mask((opt.prefix + x).display(), opt.case_sensit); else ret = new (nothrow) regular_mask(line_tools_build_regex_for_exclude_mask(opt.prefix.display(), x), opt.case_sensit); if(ret == nullptr) throw Ememory("make_exclude_path"); return ret; } static mask *make_include_path(const string & x, mask_opt opt) { mask *ret = nullptr; if(opt.file_listing) ret = new (nothrow) mask_list(x, opt.case_sensit, opt.prefix, true); else ret = new (nothrow) simple_path_mask(opt.prefix + x, opt.case_sensit); if(ret == nullptr) throw Ememory("make_include_path"); return ret; } static mask *make_ordered_mask(deque & listing, mask *(*make_include_mask) (const string & x, mask_opt opt), mask *(*make_exclude_mask)(const string & x, mask_opt opt), const path & prefix) { mask *ret_mask = nullptr; ou_mask *tmp_ou_mask = nullptr; et_mask *tmp_et_mask = nullptr; mask *tmp_mask = nullptr; mask_opt opt = prefix; try { while(!listing.empty()) { opt.read_from(listing.front()); if(listing.front().included) if(ret_mask == nullptr) // first mask { ret_mask = (*make_include_mask)(listing.front().mask, opt); if(ret_mask == nullptr) throw Ememory("make_ordered_mask"); } else // ret_mask != nullptr (need to chain to existing masks) { if(tmp_ou_mask != nullptr) { tmp_mask = (*make_include_mask)(listing.front().mask, opt); tmp_ou_mask->add_mask(*tmp_mask); delete tmp_mask; tmp_mask = nullptr; } else // need to create ou_mask { tmp_mask = (*make_include_mask)(listing.front().mask, opt); tmp_ou_mask = new (nothrow) ou_mask(); if(tmp_ou_mask == nullptr) throw Ememory("make_ordered_mask"); tmp_ou_mask->add_mask(*ret_mask); tmp_ou_mask->add_mask(*tmp_mask); delete tmp_mask; tmp_mask = nullptr; delete ret_mask; ret_mask = tmp_ou_mask; tmp_et_mask = nullptr; } } else // exclude mask if(ret_mask == nullptr) { tmp_mask = (*make_exclude_mask)(listing.front().mask, opt); ret_mask = new (nothrow) not_mask(*tmp_mask); if(ret_mask == nullptr) throw Ememory("make_ordered_mask"); delete tmp_mask; tmp_mask = nullptr; } else // ret_mask != nullptr { if(tmp_et_mask != nullptr) { tmp_mask = (*make_exclude_mask)(listing.front().mask, opt); tmp_et_mask->add_mask(not_mask(*tmp_mask)); delete tmp_mask; tmp_mask = nullptr; } else // need to create et_mask { tmp_mask = (*make_exclude_mask)(listing.front().mask, opt); tmp_et_mask = new (nothrow) et_mask(); if(tmp_et_mask == nullptr) throw Ememory("make_ordered_mask"); tmp_et_mask->add_mask(*ret_mask); tmp_et_mask->add_mask(not_mask(*tmp_mask)); delete tmp_mask; tmp_mask = nullptr; delete ret_mask; ret_mask = tmp_et_mask; tmp_ou_mask = nullptr; } } listing.pop_front(); } if(ret_mask == nullptr) { ret_mask = new (nothrow) bool_mask(true); if(ret_mask == nullptr) throw Ememory("get_args"); } } catch(...) { if(ret_mask != nullptr) { delete tmp_mask; tmp_mask = nullptr; } if(tmp_ou_mask != nullptr && tmp_ou_mask != ret_mask) { delete tmp_ou_mask; tmp_ou_mask = nullptr; } if(tmp_et_mask != nullptr && tmp_et_mask != ret_mask) { delete tmp_et_mask; tmp_et_mask = nullptr; } if(tmp_mask != nullptr) { delete tmp_mask; tmp_mask = nullptr; } throw; } return ret_mask; } static mask *make_unordered_mask(deque & listing, mask *(*make_include_mask) (const string & x, mask_opt opt), mask *(*make_exclude_mask)(const string & x, mask_opt opt), const path & prefix) { et_mask *ret_mask = new (nothrow) et_mask(); ou_mask tmp_include, tmp_exclude; mask *tmp_mask = nullptr; mask_opt opt = prefix; if(ret_mask == nullptr) throw Ememory("make_unordered_mask"); try { while(!listing.empty()) { opt.read_from(listing.front()); if(listing.front().included) { tmp_mask = (*make_include_mask)(listing.front().mask, opt); tmp_include.add_mask(*tmp_mask); delete tmp_mask; tmp_mask = nullptr; } else // excluded mask { tmp_mask = (*make_exclude_mask)(listing.front().mask, opt); tmp_exclude.add_mask(*tmp_mask); delete tmp_mask; tmp_mask = nullptr; } listing.pop_front(); } if(tmp_include.size() > 0) ret_mask->add_mask(tmp_include); else ret_mask->add_mask(bool_mask(true)); if(tmp_exclude.size() > 0) ret_mask->add_mask(not_mask(tmp_exclude)); } catch(...) { delete ret_mask; ret_mask = nullptr; throw; } return ret_mask; } static fsa_scope string_to_fsa(const string & arg) { fsa_scope ret; deque fams; line_tools_split(arg, ',', fams); ret.clear(); if(arg != "none") { for(deque::iterator it = fams.begin(); it != fams.end(); ++it) { if(*it == "extX" || *it == "ext" || *it == "extx") ret.insert(fsaf_linux_extX); else if(*it == "HFS+" || *it == "hfs+") ret.insert(fsaf_hfs_plus); else throw Erange("string_to_fsa", string(gettext("unknown FSA family: ")) + (*it)); } } return ret; } static void add_non_options(S_I argc, char * const argv[], deque & non_options) { (void)line_tools_reset_getopt(); #if HAVE_GETOPT_LONG while(getopt_long(argc, argv, OPT_STRING, get_long_opt(), nullptr) != EOF) ; #else while(getopt(argc, argv, OPT_STRING) != EOF) ; #endif for(S_I i = optind ; i < argc ; ++i) if(strcmp(argv[i],"create") == 0 || strcmp(argv[i], "extract") == 0 || strcmp(argv[i], "listing") == 0 || strcmp(argv[i], "list") == 0 || strcmp(argv[i], "test") == 0 || strcmp(argv[i], "diff") == 0 || strcmp(argv[i], "isolate") == 0 || strcmp(argv[i], "merge") == 0 || strcmp(argv[i], "reference") == 0 || strcmp(argv[i], "auxiliary") == 0 || strcmp(argv[i], "all") == 0 || strcmp(argv[i], "default") == 0) throw Erange("add_non_options", tools_printf(gettext("User target named \"%s\" is not allowed (reserved word for conditional syntax)"), argv[i])); else non_options.push_back(argv[i]); (void)line_tools_reset_getopt(); } dar-2.7.15/src/dar_suite/dar_cp.cpp0000644000175000017500000002520414636066467014024 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "line_tools.hpp" extern "C" { #if HAVE_STDIO_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_STRIN_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STDLIB_H #include #endif } #include #include "dar_suite.hpp" #include "tools.hpp" #include "cygwin_adapt.hpp" #include "dar_suite.hpp" #include "libdar.hpp" #include "thread_cancellation.hpp" #define DAR_CP_VERSION "1.3.0" using namespace libdar; using namespace std; static void show_usage(user_interaction & dialog, char *argv0); static void show_version(user_interaction & dialog, char *argv0); static int open_files(user_interaction & dialog, char *src, char *dst, int *fds, int *fdd, bool force); static int copy_max(user_interaction & dialog, int src, int dst); static void xfer_before_error(int block, char *buffer, int src, int dst); static int skip_to_next_readable(int block, char *buffer, int src, int dst, off_t & missed); /* return 0 if not found any more readable data, else return 1 */ static int normal_copy(int block, char *buffer, int src, int dst); /* return the number of copied bytes (negative value upon error, zero at end of file) */ static int little_main(shared_ptr & dialog, int argc, char * const argv[], const char **env); int main(int argc, char * const argv[], const char **env) { return dar_suite_global(argc, argv, env, "hf", #if HAVE_GETOPT_LONG nullptr, #endif '\0', // should never be met as option, thus early read the whole command-line for -j and -Q options &little_main); } static int little_main(shared_ptr & dialog, int argc, char * const argv[], const char **env) { int fds, fdd; int ret = EXIT_OK; if(argc > 1 && strcmp(argv[1],"-V") == 0) { show_version(*dialog, argv[0]); ret = EXIT_OK; } else { bool force = (argc > 1 && string(argv[1]) == string("-f")); if((argc != 3 && !force) || (argc != 4 && force) || string(argv[1]) == string("-h")) { show_usage(*dialog, argv[0]); ret = EXIT_SYNTAX; } else { char *src = force ? argv[2] : argv[1]; char *dst = force ? argv[3] : argv[2]; if(open_files(*dialog, src, dst, &fds, &fdd, force)) { try { ret = copy_max(*dialog, fds, fdd); close(fds); close(fdd); } catch(Erange & e) { ret = EXIT_BUG; // not the same meaning for dar_cp } } else ret = EXIT_ERROR; } } return ret; } static void show_usage(user_interaction & dialog, char *argv0) { dialog.message(tools_printf(gettext("usage : %s [-f] \n"), argv0)); } static void show_version(user_interaction & dialog, char *argv0) { /* C++ syntax used*/ try { string cmd; line_tools_extract_basename(argv0, cmd); // never return a nullptr pointer but dialog.message(tools_printf("\n %s version %s Copyright (C) 2002-2024 Denis Corbin\n\n\n", cmd.c_str(), DAR_CP_VERSION)); dialog.message(tools_printf(gettext(" compiled the %s with %s version %s\n"), __DATE__, CC_NAT, __VERSION__)); dialog.message(tools_printf(gettext(" %s is part of the Disk ARchive suite (Release %s)\n"), cmd.c_str(), PACKAGE_VERSION)); dialog.message(tools_printf(gettext(" %s comes with ABSOLUTELY NO WARRANTY; for details type `dar -W'."), cmd.c_str())); dialog.message(tools_printf(gettext(" This is free software, and you are welcome to redistribute it under"))); dialog.message(tools_printf(gettext(" certain conditions; type `dar -L | more' for details.\n\n"))); } catch(...) { dialog.message(tools_printf(gettext("Unexpected exception from libdar"))); throw SRC_BUG; } /* END of C++ syntax used*/ } static int open_files(user_interaction & dialog, char *src, char *dst, int *fds, int *fdd, bool force) { struct stat buf; int val = stat(dst, &buf); char *tmp = nullptr; if(val == 0 && S_ISDIR(buf.st_mode)) { tmp = (char *)malloc(strlen(src)+strlen(dst)+1+1); if(tmp == nullptr) { dialog.message(tools_printf(gettext("Memory allocation failed : %s"), strerror(errno))); return 0; } string tmp2; line_tools_extract_basename(src, tmp2); strcpy(tmp, dst); strcat(tmp, "/"); strcat(tmp, tmp2.c_str()); dst = tmp; // dst is not dynamically allocated, thus we can // loose its reference. } *fds = ::open(src, O_RDONLY|O_BINARY); if(*fds < 0) { dialog.message(tools_printf(gettext("Cannot open source file : %s"), strerror(errno))); if(tmp != nullptr) free(tmp); return 0; // error } *fdd = ::open(dst, O_WRONLY|O_CREAT|(force ? 0 : O_EXCL)|O_BINARY, 0666); if(tmp != nullptr) free(tmp); if(*fdd < 0) { dialog.message(tools_printf(gettext("Cannot open destination file : %s"), strerror(errno))); close(*fds); *fds = -1; return 0; } else return 1; } static int copy_max(user_interaction & dialog, int src, int dst) { thread_cancellation thr; #define BUF_SIZE 1024 // choosing the correct value for BUF_SIZE // too long makes I/O error very long to recover // too small makes copying under normal condition a bit slow // 1024 seems a balanced choice. // char buffer[BUF_SIZE]; int lu = 0; off_t missed = 0; off_t taille = lseek(src, 0, SEEK_END); lseek(src, 0, SEEK_SET); dialog.message(tools_printf(gettext("Starting the copy of %u byte(s)"), taille)); do { thr.check_self_cancellation(); lu = normal_copy(BUF_SIZE, buffer, src, dst); if(lu < 0) { off_t local_missed; off_t current = lseek(src, 0, SEEK_CUR); printf(gettext("Error reading source file (we are at %.2f %% of data copied), trying to read further: %s\n"), (float)(current)/(float)(taille)*100, strerror(errno)); xfer_before_error(BUF_SIZE/2, buffer, src, dst); if(skip_to_next_readable(BUF_SIZE/2, buffer, src, dst, local_missed)) { printf(gettext("Skipping done (missing %.0f byte(s)), found correct data to read, continuing the copy...\n"), (float)local_missed); missed += local_missed; lu = 1; } else { dialog.message(tools_printf(gettext("Reached End of File, no correct data could be found after the last error\n"))); missed += (taille - current); lu = 0; } } } while(lu > 0); printf(gettext("Copy finished. Missing %.0f byte(s) of data\n"), (float)missed); printf(gettext("Which is %.2f %% of the total amount of data\n"), (float)(missed)/((float)(taille))*100); return missed == 0 ? EXIT_OK : EXIT_DATA_ERROR; } static void xfer_before_error(int block, char *buffer, int src, int dst) { if(read(src, buffer, 1) == 1) { if(lseek(src, -1, SEEK_CUR) < 0) throw Erange("xfer_before_error", gettext("Cannot seek back one char")); } else return; // the error is just next char to read while(block > 1) { int lu = read(src, buffer, block); if(lu > 0) { if(write(dst, buffer, lu) < 0) throw Erange("xfer_before_error", gettext("Cannot write to destination, aborting")); } else block /= 2; } } static int skip_to_next_readable(int block, char *buffer, int src, int dst, off_t & missed) { int lu = 0; off_t min = lseek(src, 0, SEEK_CUR), max; missed = min; thread_cancellation thr; ////////////////////////// // first step: looking for a next readable (maybe not the very next readable) // we put its position in "max" ////////////////////////// do { thr.check_self_cancellation(); lu = read(src, buffer, 1); // once we will read after the end of file, read() will return 0 if(lu < 0) { if(block*2 > block) // avoid integer overflow block *= 2; lseek(src, block, SEEK_CUR); } } while(lu < 0); max = lseek(src, 0, SEEK_CUR); // the current position ////////////////////////// // second step: looking for the very next readable char between offsets min and max ////////////////////////// while(max - min > 1) { thr.check_self_cancellation(); off_t mid = (max + min) / 2; if(lseek(src, mid, SEEK_SET) < 0) { perror(gettext("Cannot seek in file")); return 0; } lu = read(src, buffer, 1); switch(lu) { case 1: // valid character read case 0: // still after EOF max = mid; break; default: // no character could be read min = mid; break; } } lseek(src, max, SEEK_SET); lu = read(src, buffer, 1); lseek(src, max, SEEK_SET); lseek(dst, max, SEEK_SET); // we eventually make a hole in dst, // which will be full of zero // see lseek man page. missed = max - missed; if(lu != 1) return 0; // could not find valid char to read else return 1; // there is valid char to read return 1; } static int normal_copy(int block, char *buffer, int src, int dst) { int lu = read(src, buffer, block); if(lu > 0) { int ecrit = 0; while(ecrit < lu) { int tmp = write(dst, buffer+ecrit, lu-ecrit); if(tmp < 0) throw Erange("normal_copy", gettext("Cannot write to destination, aborting")); else if(tmp == 0) printf(gettext("Non fatal error while writing to destination file, retrying\n")); else ecrit += tmp; } } return lu; } dar-2.7.15/src/dar_suite/crit_action_cmd_line.hpp0000644000175000017500000000506514636066467016734 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file crit_action_cmd_line.hpp /// \brief contains routines to convert command-line overwriting criterium to their crit_action counterparts /// \ingroup CMDLINE #ifndef CRIT_ACTION_CMD_LINE_HPP #define CRIT_ACTION_CMD_LINE_HPP #include "../my_config.h" #include "libdar.hpp" /// \addtogroup CMDLINE /// @{ /// canonizes a criterium description string /// \param[in] argument is a not canonized criterium description string /// \return a canonized description string /// \note criterium description strings when not canonized may include spaces /// tabs and carriage return for readability. Canonizing a criterium description string /// is to remove all these non significant characters. extern std::string crit_action_canonize_string(const std::string & argument); /// creates a criterium object as defined by the command line's given string /// \param[in] dialog for user interaction /// \param[in] argument is a *canonized* criterium argument /// \param[in] hourshift the hourshift used to compare dates "more recent than" /// \return a criterium object /// \note this function is recursive, it may throw Erange exception in case of syntaxical error /// \note second point, the returned object is dynamically allocated, this is the duty of the caller /// to release its memory calling the delete operator. extern const libdar::crit_action * crit_action_create_from_string(libdar::user_interaction & dialog, const std::string & argument, const libdar::infinint & hourshift); /// @} #endif dar-2.7.15/src/dar_suite/my_getopt_long.h0000644000175000017500000000504514636066467015270 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file my_getopt_long.h /// \brief may lead to the definition of getopt_long to solve declaration conflicts in and /// \ingroup CMDLINE #ifndef MY_GETOPT_LONG_H #define MY_GETOPT_LONG_H // getopt may be declated in on systems like FreeBSD. // if you want to use libgnugetopt you need to include // on this system. Thus a conflict appear because the getopt is // declared twice. To avoid you either have not to include // or not to include . But neither the first nor the // second is acceptable, because we need other stuf declared in // (open() & so on) and stuf declared in // (like getopt_long which is gnu typical). // // to solve this problem, I have extracted the gnu getopt_long // as declared under Linux in the present file. When getopt is // declared in it is still possible to include the // current file in place of , to get getopt_long available // // at linking time, if libgnugetopt is available we use it // // see getopt_decision.hpp # define no_argument 0 # define required_argument 1 # define optional_argument 2 struct option { # if defined __STDC__ && __STDC__ const char *name; # else char *name; # endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); #endif dar-2.7.15/src/dar_suite/hide_file.cpp0000644000175000017500000001121114636066467014475 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include "hide_file.hpp" using namespace libdar; #define CHECK_INIT if(!is_init) init() hide_file::hide_file(generic_file &f) : generic_file(gf_read_only) { if(f.get_mode() == gf_write_only) throw Erange("hide_file::hide_file", gettext("hide_file cannot be initialized with write-only file")); ref = &f; if(ref == nullptr) throw SRC_BUG; // nullptr argument given is_init = false; pos_index = 0; pos_relicat = 0; } bool hide_file::skip(const infinint & pos) { CHECK_INIT; if(is_terminated()) throw SRC_BUG; U_I it = 0; while(it < morceau.size() && morceau[it].offset + morceau[it].longueur - 1 < pos) ++it; if(it >= morceau.size()) return false; if(morceau[it].offset > pos) throw SRC_BUG; // morceau has a hole in it. pos_index = it; pos_relicat = pos - morceau[it].offset; return true; } bool hide_file::skip_to_eof() { CHECK_INIT; if(is_terminated()) throw SRC_BUG; pos_index = morceau.size(); return true; } bool hide_file::skip_relative(S_I x) { CHECK_INIT; if(is_terminated()) throw SRC_BUG; if(x > 0) { infinint my_x = x; while(my_x > 0 && pos_index < morceau.size()) { if(pos_relicat + my_x >= morceau[pos_index].longueur) { my_x -= morceau[pos_index].longueur - pos_relicat; pos_relicat = 0; ++pos_index; } else { pos_relicat += my_x; my_x = 0; } } return pos_index < morceau.size(); } else if(x < 0) { infinint my_x = -x; // make x a positive value while(my_x > 0 && (pos_index < morceau.size() || pos_relicat > 0)) { if(my_x > pos_relicat) { --pos_index; if(pos_index < morceau.size()) { my_x -= pos_relicat; pos_relicat = morceau[pos_index].longueur; } else { pos_index = 0; return false; } } else pos_relicat -= my_x; return true; } } return true; } infinint hide_file::get_position() const { CHECK_INIT; if(is_terminated()) throw SRC_BUG; if(pos_index >= morceau.size()) if(morceau.empty()) return 0; // empty virtual file else return morceau.back().offset + morceau.back().longueur; else return morceau[pos_index].offset + pos_relicat; } U_I hide_file::inherited_read(char *a, U_I size) { CHECK_INIT; if(is_terminated()) throw SRC_BUG; U_I lu = 0, tmp_lu = 0; size_t maxlire; size_t reste; while(lu < size && pos_index < morceau.size()) { maxlire = 0; (morceau[pos_index].longueur - pos_relicat).unstack(maxlire); reste = size - lu; maxlire = maxlire > reste ? reste : maxlire; ref->skip(morceau[pos_index].debut+pos_relicat); tmp_lu = ref->read(a+lu, maxlire); if(tmp_lu == 0 && maxlire > 0) throw SRC_BUG; lu += tmp_lu; pos_relicat += lu; if(pos_relicat >= morceau[pos_index].longueur) { pos_index++; pos_relicat = 0; } } return lu; } void hide_file::inherited_write(const char *a, size_t size) { CHECK_INIT; throw SRC_BUG; // hide_file alsways is read-only ! } void hide_file::init() const { hide_file *me = const_cast(this); if(me == nullptr) throw SRC_BUG; me->fill_morceau(); me->is_init = true; } dar-2.7.15/src/dar_suite/dar_manager.cpp0000644000175000017500000014476014636067146015040 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_ERRNO_H #include #endif #include "getopt_decision.h" } // end extern "C" #include #include #include #include "dar_suite.hpp" #include "libdar.hpp" #include "tools.hpp" #include "cygwin_adapt.hpp" #include "no_comment.hpp" #include "line_tools.hpp" #include "fichier_local.hpp" using namespace libdar; #define DAR_MANAGER_VERSION "1.9.0" #define ONLY_ONCE "Only one -%c is allowed, ignoring this extra option" #define MISSING_ARG "Missing argument to -%c" #define INVALID_ARG "Invalid argument given to -%c (requires integer)" #define OPT_STRING "C:B:A:lD:b:p:od:ru:f:shVm:vQjw:ie:c@:N;:ka:9:z:" enum operation { none_op, create, add, listing, del, chbase, where, options, dar, restore, used, files, stats, moving, interactive, check, batch }; static S_I little_main(shared_ptr & dialog, S_I argc, char *const argv[], const char **env); static bool command_line(shell_interaction & dialog, S_I argc, char * const argv[], operation & op, string & base, string & arg, S_I & num, vector & rest, archive_num & num2, infinint & date, bool & verbose, bool & ignore_dat_options, bool & even_when_removed, bool & check_order, compression & algozip, U_I & compression_level, bool & change_compression, bool recursive); // true if called from op_batch static void show_usage(shell_interaction & dialog, const char *command); static void show_version(shell_interaction & dialog, const char *command); #if HAVE_GETOPT_LONG static const struct option *get_long_opt(); #endif static void op_create(shared_ptr & dialog, const string & base, compression algozip, U_I compr_level, bool info_details); static void op_add(shared_ptr & dialog, database *dat, const string &arg, string fake, const infinint & min_digits, bool info_details); static void op_listing(shell_interaction & dialog, const database *dat, bool info_details); static void op_del(shared_ptr & dialog, database *dat, S_I min, archive_num max, bool info_details); static void op_chbase(shared_ptr & dialog, database *dat, S_I num, const string & arg, bool info_details); static void op_where(shared_ptr & dialog, database *dat, S_I num, const string & arg, bool info_details); static void op_options(shared_ptr & dialog, database *dat, const vector & rest, bool info_details); static void op_dar(shared_ptr & dialog, database *dat, const string & arg, bool info_details); static void op_restore(shared_ptr & dialog, database *dat, const vector & rest, const infinint & date, const string & options_for_dar, bool info_details, bool early_release, bool ignore_dar_options_in_base, bool even_when_removed); static void op_used(shell_interaction & dialog, const database *dat, S_I num, bool info_details); static void op_files(shell_interaction & dialog, const database *dat, const string & arg, bool info_details); static void op_stats(shell_interaction & dialog, const database *dat, bool info_details); static void op_move(shared_ptr & dialog, database *dat, S_I src, archive_num dst, bool info_details); static void op_interactive(shared_ptr & dialog, database *dat, string base); static void op_check(shared_ptr & dialog, const database *dat, bool info_details); static void op_batch(shared_ptr & dialog, database *dat, const string & filename, bool info_details); static database *read_base(shared_ptr & dialog, const string & base, bool partial, bool partial_read_only, bool check_order); static void write_base(shared_ptr & dialog, const string & filename, const database *base, bool overwrite, bool change_compression, compression new_compression, U_I compression_level); static vector read_vector(shared_ptr & dialog); static void finalize(shared_ptr & dialog, operation op, database *dat, const string & base, bool info_details, bool change_compression, compression new_compression, U_I compression_level); static void action(shared_ptr & dialog, operation op, database *dat, const string & arg, S_I num, const vector & rest, archive_num num2, const infinint & date, const string & base, bool info_details, bool early_release, bool ignore_database_options, bool even_when_removed); static void signed_int_to_archive_num(S_I input, archive_num &num, bool & positive); int main(S_I argc, char *const argv[], const char **env) { return dar_suite_global(argc, argv, env, OPT_STRING, #if HAVE_GETOPT_LONG get_long_opt(), #endif 'o', // stop looking for -j and -Q once -o option is met on command-line &little_main); } S_I little_main(shared_ptr & dialog, S_I argc, char * const argv[], const char **env) { operation op; string base; string arg; S_I num; archive_num num2; vector rest; bool info_details; infinint date; database *dat = nullptr; bool partial_read = false; bool partial_read_only = false; bool ignore_dat_options; bool even_when_removed; bool check_order; compression algozip; U_I compression_level; bool change_compression; shell_interaction *shelli = dynamic_cast(dialog.get()); if(!dialog) throw SRC_BUG; if(shelli == nullptr) throw SRC_BUG; shelli->change_non_interactive_output(cout); if(!command_line(*shelli, argc, argv, op, base, arg, num, rest, num2, date, info_details, ignore_dat_options, even_when_removed, check_order, algozip, compression_level, change_compression, false)) return EXIT_SYNTAX; if(op == none_op) throw SRC_BUG; switch(op) { case create: break; case add: case del: case restore: case used: case files: case stats: case moving: case interactive: case check: case batch: partial_read = false; break; case listing: partial_read_only = true; if(change_compression) throw Erange("little_main", gettext("Cannot change compression when the requested operation is a read operation")); break; case chbase: case where: case options: case dar: partial_read = true; if(change_compression) throw Erange("little_main", gettext("Cannot change compression when the requested operation only modifies the database header")); break; default: throw SRC_BUG; } if(op == create) op_create(dialog, base, algozip, compression_level, info_details); else { if(info_details) { if(partial_read) dialog->message(gettext("Decompressing and loading database header to memory...")); else dialog->message(gettext("Decompressing and loading database to memory...")); } dat = read_base(dialog, base, partial_read, partial_read_only, check_order); try { try { action(dialog, op, dat, arg, num, rest, num2, date, base, info_details, true, ignore_dat_options, even_when_removed); finalize(dialog, op, dat, base, info_details, change_compression, algozip, compression_level); } catch(Edata & e) { dialog->message(string(gettext("Error met while processing operation: ")) + e.get_message()); finalize(dialog, op, dat, base, info_details, change_compression, algozip, compression_level); throw; } } catch(...) { if(dat != nullptr) delete dat; throw; } if(dat != nullptr) delete dat; } return EXIT_OK; } static bool command_line(shell_interaction & dialog, S_I argc, char *const argv[], operation & op, string & base, string & arg, S_I & num, vector & rest, archive_num & num2, infinint & date, bool & verbose, bool & ignore_dat_options, bool & even_when_removed, bool & check_order, compression & algozip, U_I & compression_level, bool & change_compression, bool recursive) { S_I lu, min; U_I max; U_I compr_bs; // block compression is not used in dar_manager enum { date_not_set, date_set_by_w, date_set_by_9 } date_set = date_not_set; base = arg = ""; num = num2 = 0; rest.clear(); op = none_op; verbose = false; string chem, filename; date = 0; ignore_dat_options = false; even_when_removed = false; check_order = true; algozip = compression::gzip; compression_level = 9; change_compression = false; string extra = ""; try { (void)line_tools_reset_getopt(); #if HAVE_GETOPT_LONG while((lu = getopt_long(argc, argv, OPT_STRING, get_long_opt(), nullptr)) != EOF) #else while((lu = getopt(argc, argv, OPT_STRING)) != EOF) #endif { switch(lu) { case 'C': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = create; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); base = optarg; break; case 'B': if(recursive) throw Erange("command_line", tools_printf(gettext("-B option cannot be given inside a batch file"))); if(base != "") throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); base = optarg; break; case 'A': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = add; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); line_tools_split_path_basename(optarg, chem, filename); line_tools_check_basename(dialog, chem, filename, EXTENSION, false); arg = (path(chem).append(filename)).display(); break; case 'l': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = listing; break; case 'D': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = del; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); try { line_tools_read_range(string(optarg), min, max); } catch(Edeci & e) { throw Erange("command_line", tools_printf(gettext(INVALID_ARG), char(lu))); } num = min; num2 = max; break; case 'b': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = chbase; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); num = line_tools_str2signed_int(optarg); break; case 'p': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = where; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); num = line_tools_str2signed_int(optarg); break; case 'o': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = options; break; case 'd': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = dar; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); arg = optarg; break; case 'r': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = restore; break; case 'u': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = used; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); num = line_tools_str2signed_int(optarg); break; case 'f': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = files; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); arg = optarg; break; case 's': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = stats; break; case 'm': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = moving; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); num = tools_str2int(optarg); break; case 'h': show_usage(dialog, argv[0]); return false; case 'V': show_version(dialog, argv[0]); return false; case 'v': verbose = true; break; case 'Q': break; // ignore this option already parsed during initialization (dar_suite.cpp) case 'w': if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); date = line_tools_convert_date(optarg); date_set = date_set_by_w; break; case 'i': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = interactive; break; case 'c': if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = check; break; case 'e': if(extra != "") throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); extra = optarg; break; case '@': if(recursive) throw Erange("command_line", tools_printf(gettext("Running batch file from a batch file is not allowed"))); if(op != none_op) throw Erange("command_line", tools_printf(gettext(ONLY_ONCE), char(lu))); op = batch; if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); arg = optarg; break; case 'N': ignore_dat_options = true; break; case 'k': even_when_removed = true; break; case '9': try { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci tmp = string(optarg); date = tmp.computer(); date_set = date_set_by_9; } catch(Edeci & e) { throw Erange("command_line", tools_printf(gettext("invalid number given to -9 option: %s"), optarg)); } break; case 'a': if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); if(strcasecmp("i", optarg) == 0 || strcasecmp("ignore-order", optarg) == 0) check_order = false; else throw Erange("command_line", tools_printf(gettext(INVALID_ARG), char(lu))); break; case 'z': if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext(MISSING_ARG), char(lu))); line_tools_split_compression_algo(optarg, 1, // no prefix (used for block anyway) algozip, compression_level, compr_bs); // we ignore this in dar_manager change_compression = true; break; case ':': throw Erange("get_args", tools_printf(gettext(MISSING_ARG), char(optopt))); case '?': throw Erange("get_args", tools_printf(gettext("Ignoring unknown option -%c"), char(optopt))); default: throw Erange("get_args", tools_printf(gettext("Ignoring unknown option -%c"), char(lu))); } if(lu == 'o' || lu == 'r') break; // stop reading arguments } // related to bug #1598138 on sourceforge // when providing -o'-B dar.dcf' on command line getopt // finds -o option with no argument and keep optind pointing on // the -o'B dar dcf' argument. We must thus tail out the leading -o // from this argument before feeding the 'rest' list variable if(optind < argc && strncmp(argv[optind], "-o", 2) == 0) { rest.push_back(argv[optind]+2); // argv[optind] is a 'char *' applying '+2' to it skips the 2 leading char '-o'. optind++; } for(S_I i = optind; i < argc; ++i) rest.push_back(argv[i]); // sanity checks if(extra != "" && op != restore) { dialog.message(gettext("-e option is only available when using -r option, aborting")); return false; } switch(date_set) { case date_not_set: break; case date_set_by_w: if(op != restore) { dialog.message(gettext("-w option is only valid with -r option, ignoring it")); date = 0; } break; case date_set_by_9: if(op != add) { dialog.message(gettext("-9 option is only valid with -A option, ignoring it")); date = 0; } break; default: throw SRC_BUG; } switch(op) { case none_op: dialog.message(gettext("No action specified, aborting")); return false; case create: case listing: case del: case dar: case used: case files: case stats: case interactive: case check: if(!rest.empty()) dialog.message(gettext("Ignoring extra arguments on command line")); break; case add: if(rest.size() > 1) dialog.message(gettext("Ignoring extra arguments on command line")); if(date_set == date_not_set) // --min-digits not set { line_tools_check_min_digits(dialog, chem, filename, EXTENSION, date); date_set = date_set_by_9; } break; case chbase: case where: if(rest.size() != 1) { dialog.message(gettext("Missing argument to command line, aborting")); return false; } arg = rest[0]; rest.clear(); break; case restore: for(unsigned int i = 0; i < rest.size(); ++i) if(!path(rest[i]).is_relative()) throw Erange("command_line", gettext("Arguments to -r must be relative path (never begin by '/')")); arg = extra; break; case options: break; case moving: if(rest.size() != 1) { dialog.message(gettext("Missing argument to command line, aborting")); return false; } num2 = tools_str2int(rest[0]); rest.clear(); break; case batch: break; default: throw SRC_BUG; } if(base == "" && !recursive) { dialog.message(gettext("No database specified, aborting")); return false; } } catch(Erange & e) { dialog.message(string(gettext("Parse error on command line (or included files): ")) + e.get_message()); return false; } return true; } static void op_create(shared_ptr & dialog, const string & base, compression algozip, U_I compr_level, bool info_details) { database dat(dialog); // created empty; if(info_details) { dialog->message(gettext("Creating file...")); dialog->message(gettext("Formatting file as an empty database...")); } write_base(dialog, base, &dat, false, true, algozip, compr_level); if(info_details) dialog->message(gettext("Database has been successfully created empty.")); } static void op_add(shared_ptr & dialog, database *dat, const string &arg, string fake, const infinint & min_digits, bool info_details) { thread_cancellation thr; string arch_path, arch_base; bool date_order_problem = false; archive_options_read read_options; if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Reading catalogue of the archive to add...")); line_tools_split_path_basename(arg, arch_path, arch_base); read_options.set_info_details(info_details); read_options.set_slice_min_digits(min_digits); archive *arch = new (nothrow) archive(dialog, path(arch_path), arch_base, EXTENSION, read_options); if(arch == nullptr) throw Ememory("dar_manager.cpp:op_add"); try { string chemin, b; thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Updating database with catalogue...")); if(fake == "") fake = arg; line_tools_split_path_basename(fake, chemin, b); dat->add_archive(*arch, chemin, b, database_add_options()); thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Checking date ordering of files between archives...")); date_order_problem = !dat->check_order(); thr.check_self_cancellation(); } catch(...) { if(arch != nullptr) delete arch; throw; } delete arch; if(date_order_problem) throw Edata(gettext("Some files do not follow chronological order when archive index increases withing the database, this can lead dar_manager to restored a wrong version of these files")); } static void op_listing(shell_interaction & dialog, const database *dat, bool info_details) { if(dat == nullptr) throw SRC_BUG; dialog.database_show_contents(*dat); } static void op_del(shared_ptr & dialog, database *dat, S_I min, archive_num max, bool info_details) { thread_cancellation thr; database_remove_options opt; bool sign_plus; archive_num rmin; signed_int_to_archive_num(min, rmin, sign_plus); opt.set_revert_archive_numbering(!sign_plus); if(!sign_plus) max = rmin; if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Removing information from the database...")); dat->remove_archive(rmin, max, opt); thr.check_self_cancellation(); } static void op_chbase(shared_ptr & dialog, database *dat, S_I num, const string & arg, bool info_details) { thread_cancellation thr; database_change_basename_options opt; bool sign_plus; archive_num rnum; signed_int_to_archive_num(num, rnum, sign_plus); opt.set_revert_archive_numbering(!sign_plus); if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Changing database header information...")); dat->change_name(rnum, arg, opt); thr.check_self_cancellation(); } static void op_where(shared_ptr & dialog, database *dat, S_I num, const string & arg, bool info_details) { thread_cancellation thr; database_change_path_options opt; bool sign_plus; archive_num rnum; signed_int_to_archive_num(num, rnum, sign_plus); opt.set_revert_archive_numbering(!sign_plus); if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Changing database header information...")); dat->set_path(rnum, arg, opt); thr.check_self_cancellation(); } static void op_options(shared_ptr & dialog, database *dat, const vector & rest, bool info_details) { thread_cancellation thr; if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Changing database header information...")); dat->set_options(rest); thr.check_self_cancellation(); } static void op_dar(shared_ptr & dialog, database *dat, const string & arg, bool info_details) { thread_cancellation thr; if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Changing database header information...")); dat->set_dar_path(arg); thr.check_self_cancellation(); } static void op_restore(shared_ptr & dialog, database *dat, const vector & rest, const infinint & date, const string & options_for_dar, bool info_details, bool early_release, bool ignore_dar_options_in_base, bool even_when_removed) { thread_cancellation thr; vector options; database_restore_options dat_opt; if(dat == nullptr) throw SRC_BUG; line_tools_split_in_words(options_for_dar, options); thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Looking in archives for requested files, classifying files archive by archive...")); dat_opt.set_early_release(early_release); dat_opt.set_info_details(info_details); dat_opt.set_date(date); dat_opt.set_extra_options_for_dar(options); dat_opt.set_ignore_dar_options_in_database(ignore_dar_options_in_base); dat_opt.set_even_when_removed(even_when_removed); dat->restore(rest, dat_opt); } static void op_used(shell_interaction & dialog, const database *dat, S_I num, bool info_details) { thread_cancellation thr; database_used_options opt; bool sign_plus; archive_num rnum; signed_int_to_archive_num(num, rnum, sign_plus); opt.set_revert_archive_numbering(!sign_plus); if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); dialog.database_show_files(*dat, rnum, opt); } static void op_files(shell_interaction & dialog, const database *dat, const string & arg, bool info_details) { thread_cancellation thr; if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); dialog.database_show_version(*dat, arg); } static void op_stats(shell_interaction & dialog, const database *dat, bool info_details) { thread_cancellation thr; if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); if(info_details) dialog.message(gettext("Computing statistics...")); dialog.database_show_statistics(*dat); } static void op_move(shared_ptr & dialog, database *dat, S_I src, archive_num dst, bool info_details) { thread_cancellation thr; bool date_order_problem = false; if(src <= 0) throw Erange("op_move", gettext("Negative number or zero not allowed when moving an archive inside a database")); if(dat == nullptr) throw SRC_BUG; thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Changing database information...")); dat->set_permutation(src, dst); thr.check_self_cancellation(); if(info_details) dialog->message(gettext("Checking date ordering of files between archives...")); date_order_problem = !dat->check_order(); thr.check_self_cancellation(); if(date_order_problem) throw Edata(gettext("Some files do not follow chronological order when archive index increases withing the database, this can lead dar_manager to restored a wrong version of these files")); } static void show_usage(shell_interaction & dialog, const char *command) { dialog.printf("usage :\n\n"); dialog.printf("\tdar_manager [options] -C [/]\n"); dialog.printf("\tdar_manager [options] -B [/] -A [/] [[/]]\n"); dialog.printf("\tdar_manager [options] -B [/] -l\n"); dialog.printf("\tdar_manager [options] -B [/] -D \n"); dialog.printf("\tdar_manager [options] -B [/] -b \n"); dialog.printf("\tdar_manager [options] -B [/] -p \n"); dialog.printf("\tdar_manager [options] -B [/] -o [list of options to pass to dar]\n"); dialog.printf("\tdar_manager [options] -B [/] -d []\n"); dialog.printf("\tdar_manager [options] -B [/] [-w ] [-e \"\"] -r [list of files to restore]\n"); dialog.printf("\tdar_manager [options] -B [/] -u \n"); dialog.printf("\tdar_manager [options] -B [/] -f file\n"); dialog.printf("\tdar_manager [options] -B [/] -s\n"); dialog.printf("\tdar_manager [options] -B [/] -m \n"); dialog.printf("\tdar_manager [options] -B [/] -i\n"); dialog.printf("\tdar_manager [options] -B [/] -c\n"); dialog.printf("\tdar_manager [options] -B [/] -@ \n"); dialog.printf("\tdar_manager -h\n"); dialog.printf("\tdar_manager -V\n"); dialog.printf("\n"); dialog.printf(gettext("\n")); dialog.printf(gettext("Commands:\n")); dialog.printf(gettext(" -C creates an empty database\n")); dialog.printf(gettext(" -B specify the database to use (read or modify)\n")); dialog.printf(gettext(" -A add an archive to the database\n")); dialog.printf(gettext(" -l\t\t gives information about the archive compiled in the database\n")); dialog.printf(gettext(" -D delete an archive from the database\n")); dialog.printf(gettext(" -b \t change the basename to use for the give archive number\n")); dialog.printf(gettext(" -p \t change the path to use for the given archive number\n")); dialog.printf(gettext(" -o specify a list of option to always pass to dar\n")); dialog.printf(gettext(" -d specify the path to dar\n")); dialog.printf(gettext(" -r \t restores the given files\n")); dialog.printf(gettext(" -w \t only with -r, restores in state just before the given date\n")); dialog.printf(gettext(" \t date format: [[[year/]month]/day-]hour:minute[:second]\n")); dialog.printf(gettext(" -u \t list the most recent files contained in the given archive\n")); dialog.printf(gettext(" -f \t list the archives where the given file is present\n")); dialog.printf(gettext(" -s\t\t shows the number of most recent file by archive\n")); dialog.printf(gettext(" -m \t move an archive within a given database.\n")); dialog.printf(gettext(" -i\t\t user interactive mode\n")); dialog.printf(gettext(" -c\t\t check database for dates order\n")); dialog.printf(gettext(" -@ execute on a given database a batch of action as defined by\n")); dialog.printf(gettext("\t\t the provided file.\n")); dialog.printf(gettext(" -h\t\t displays this help information\n")); dialog.printf(gettext(" -V\t\t displays software version\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("Options:\n")); dialog.printf(gettext(" -v\t\t display more information about what is going on\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("See man page for more options.\n")); } static void show_version(shell_interaction & dialog, const char *command_name) { string name; line_tools_extract_basename(command_name, name); U_I maj, med, min; get_version(maj, med, min); dialog.change_non_interactive_output(cout); dialog.printf("\n %s version %s, Copyright (C) 2002-2024 Denis Corbin\n", name.c_str(), DAR_MANAGER_VERSION); dialog.message(string(" ") + dar_suite_command_line_features() + "\n"); if(maj > 2) dialog.printf(gettext(" Using libdar %u.%u.%u built with compilation time options:\n"), maj, med, min); else dialog.printf(gettext(" Using libdar %u.%u built with compilation time options:\n"), maj, min); line_tools_display_features(dialog); dialog.printf("\n"); dialog.printf(gettext(" compiled the %s with %s version %s\n"), __DATE__, CC_NAT, __VERSION__); dialog.printf(gettext(" %s is part of the Disk ARchive suite (Release %s)\n"), name.c_str(), PACKAGE_VERSION); dialog.message(tools_printf(gettext(" %s comes with ABSOLUTELY NO WARRANTY; for details\n type `%s -W'."), name.c_str(), "dar") + tools_printf(gettext(" This is free software, and you are welcome\n to redistribute it under certain conditions;")) + tools_printf(gettext(" type `%s -L | more'\n for details.\n\n"), "dar")); dialog.printf(""); } #if HAVE_GETOPT_LONG static const struct option *get_long_opt() { static const struct option ret[] = { {"create", required_argument, nullptr, 'C'}, {"base", required_argument, nullptr, 'B'}, {"add", required_argument, nullptr, 'A'}, {"list", no_argument, nullptr, 'l'}, {"delete", required_argument, nullptr, 'D'}, {"basename", required_argument, nullptr, 'b'}, {"path", required_argument, nullptr, 'p'}, {"options", no_argument, nullptr, 'o'}, {"dar", no_argument, nullptr, 'd'}, {"restore", no_argument, nullptr, 'r'}, {"used", required_argument, nullptr, 'u'}, {"file", required_argument, nullptr, 'f'}, {"stats", no_argument, nullptr, 's'}, {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'V'}, {"verbose", no_argument, nullptr, 'v'}, {"jog", no_argument, nullptr, 'j'}, {"when", required_argument, nullptr, 'w'}, {"interactive", no_argument, nullptr, 'i'}, {"extra", required_argument, nullptr, 'e'}, {"check", no_argument, nullptr, 'c'}, {"batch", required_argument, nullptr, '@'}, {"ignore-options-in-base", no_argument, nullptr, 'N'}, {"min-digits", required_argument, nullptr, '9'}, {"ignore-when-removed", no_argument, nullptr, 'k'}, {"alter", required_argument, nullptr, 'a'}, {"compression", required_argument, nullptr, 'z'}, { nullptr, 0, nullptr, 0 } }; return ret; } #endif static database *read_base(shared_ptr & dialog, const string & base, bool partial, bool partial_read_only, bool check_order) { database *ret = nullptr; try { database_open_options dat_opt; dat_opt.set_warn_order(check_order); dat_opt.set_partial(partial); dat_opt.set_partial_read_only(partial_read_only); ret = new (nothrow) database(dialog, base, dat_opt); if(ret == nullptr) throw Ememory("read_base"); } catch(Erange & e) { string msg = string(gettext("Corrupted database :"))+e.get_message(); dialog->message(msg); throw Edata(msg); } return ret; } static void write_base(shared_ptr & dialog, const string & filename, const database *base, bool overwrite, bool change_compression, compression algozip, U_I compression_level) { thread_cancellation thr; database_dump_options dat_opt; dat_opt.set_overwrite(overwrite); if(change_compression) { base->set_compression(algozip); base->set_compression_level(compression_level); } thr.block_delayed_cancellation(true); base->dump(filename, dat_opt); thr.block_delayed_cancellation(false); } static void op_interactive(shared_ptr & dialog, database *dat, string base) { char choice; bool saved = true; thread_cancellation thr; archive_num num, num2; S_I tmp_si; bool tmp_sign; string input, input2; archive *arch = nullptr; vector vectinput; bool change_compression = false; compression zipname; U_I compr_level = 9; U_I more = 25; database_change_basename_options opt_change_name; database_change_path_options opt_change_path; database_remove_options opt_remove; database_used_options opt_used; shell_interaction *shelli = dynamic_cast(dialog.get()); if(dat == nullptr) throw SRC_BUG; if(shelli == nullptr) throw SRC_BUG; thr.check_self_cancellation(); do { try { archive_options_read read_options; // diplay choice message shelli->warning_with_more(0); dialog->printf(gettext("\n\n\t Dar Manager Database used [%s] : %S\n"), saved ? gettext("Saved") : gettext("Not Saved"), &base); if(more > 0) dialog->printf(gettext("\t Pause each %d line of output\n\n"), more); else dialog->printf(gettext("\t No pause in output\n\n")); dialog->printf(gettext(" l : list database contents \t A : Add an archive\n")); dialog->printf(gettext(" u : list archive contents \t D : Remove an archive\n")); dialog->printf(gettext(" f : give file localization \t m : modify archive order\n")); dialog->printf(gettext(" p : modify path of archives \t b : modify basename of archives\n")); dialog->printf(gettext(" d : path to dar \t o : options to dar\n")); dialog->printf(gettext(" w : write changes to file \t s : database statistics\n")); dialog->printf(gettext(" c : check date order \t z : change compression algorithm\n")); dialog->printf(gettext(" a : Save as \t n : pause each 'n' line (zero for no pause)\n\n")); dialog->printf(gettext(" q : quit\n\n")); dialog->printf(gettext(" Choice: ")); // user's choice selection shelli->read_char(choice); thr.check_self_cancellation(); dialog->printf("\n\n"); // performing requested action shelli->warning_with_more(more); switch(choice) { case 'l': shelli->database_show_contents(*dat); break; case 'u': input = dialog->get_string(gettext("Archive number: "), true); tmp_si = line_tools_str2signed_int(input); signed_int_to_archive_num(tmp_si, num, tmp_sign); opt_used.set_revert_archive_numbering(!tmp_sign); shelli->database_show_files(*dat, num, opt_used); break; case 'f': input = dialog->get_string(gettext("File to look for: "), true); shelli->database_show_version(*dat, input); break; case 'b': input = dialog->get_string(gettext("Archive number to modify: "), true); tmp_si = line_tools_str2signed_int(input); signed_int_to_archive_num(tmp_si, num, tmp_sign); opt_change_name.set_revert_archive_numbering(!tmp_sign); input = dialog->get_string(tools_printf(gettext("New basename for archive number %d: "), tmp_si), true); dat->change_name(num, input, opt_change_name); saved = false; break; case 'd': input = dialog->get_string(gettext("Path to dar (empty string to use the default from PATH variable): "), true); dat->set_dar_path(input); saved = false; break; case 'z': input = dialog->get_string(gettext("New compression algorithm to use (none, gzip, bzip2, lzo, xz, zstd, lz4...)? "), true); zipname = string2compression(input); input = dialog->get_string(gettext("Compression level to use (usually an integer from 1 to 9)? "), true); tmp_si = line_tools_str2signed_int(input); if(tmp_si < 1) throw Erange("op_interactive", gettext("Compression level must be strictly positive")); compr_level = (U_I)tmp_si; dialog->message(gettext("Note: the new compression algorithm/level will be used when saving the database")); change_compression = true; saved = false; break; case 'w': dialog->message(gettext("Compressing and writing back database to file...")); write_base(dialog, base, dat, true, change_compression, zipname, compr_level); saved = true; break; case 'a': input = dialog->get_string(gettext("New database name: "), true); dialog->message(gettext("Compressing and writing back database to file...")); write_base(dialog, input, dat, false, change_compression, zipname, compr_level); base = input; saved = true; break; case 'A': input = dialog->get_string(gettext("Archive basename (or extracted catalogue basename) to add: "), true); dialog->message(gettext("Reading catalogue of the archive to add...")); line_tools_split_path_basename(input, input, input2); read_options.clear(); read_options.set_info_details(true); arch = new (nothrow) archive(dialog, path(input), input2, EXTENSION, read_options); if(arch == nullptr) throw Ememory("dar_manager.cpp:op_interactive"); try { dialog->message(gettext("Updating database with catalogue...")); dat->add_archive(*arch, input, input2, database_add_options()); thr.check_self_cancellation(); dialog->message(gettext("Checking date ordering of files between archives...")); (void)dat->check_order(); } catch(...) { delete arch; arch = nullptr; throw; } delete arch; arch = nullptr; saved = false; break; case 'D': input = dialog->get_string(gettext("Archive number to remove: "), true); tmp_si = line_tools_str2signed_int(input); signed_int_to_archive_num(tmp_si, num, tmp_sign); opt_remove.set_revert_archive_numbering(!tmp_sign); dialog->pause(tools_printf(gettext("Are you sure to remove archive number %d ?"), tmp_si)); dialog->message(gettext("Removing information from the database...")); dat->remove_archive(num, num, opt_remove); saved = false; break; case 'm': input = dialog->get_string(gettext("Archive number to move: "), true); num = tools_str2int(input); input = dialog->get_string(gettext("In which position to insert this archive: "), true); num2 = tools_str2int(input); dat->set_permutation(num, num2); thr.check_self_cancellation(); dialog->message(gettext("Checking date ordering of files between archives...")); dat->check_order(); saved = false; break; case 'p': input = dialog->get_string(gettext("Archive number who's path to modify: "), true); tmp_si = line_tools_str2signed_int(input); signed_int_to_archive_num(tmp_si, num, tmp_sign); opt_change_path.set_revert_archive_numbering(!tmp_sign); input = dialog->get_string(tools_printf(gettext("New path to give to archive number %d: "), tmp_si), true); dat->set_path(num, input, opt_change_path); saved = false; break; case 'o': vectinput = read_vector(dialog); dat->set_options(vectinput); saved = false; break; case 's': dialog->message(gettext("Computing statistics...")); shelli->database_show_statistics(*dat); break; case 'n': input = dialog->get_string(gettext("How much line to display at once: "), true); more = tools_str2int(input); break; case 'c': dialog->message(gettext("Checking file's dates ordering...")); (void)dat->check_order(); break; case 'q': if(!saved) { try { dialog->pause(gettext("Database not saved, Do you really want to quit ?")); dialog->printf(gettext("Continuing the action under process which is to exit... so we exit!")); } catch(Euser_abort & e) { choice = ' '; } } break; default: dialog->printf(gettext("Unknown choice\n")); } } catch(Ethread_cancel & e) { bool quit = false; if(!saved) { try { dialog->pause(gettext("Database not saved, Do you really want to quit ?")); dialog->printf(gettext("Continuing the action under process which is to exit... so we exit!")); quit = true; } catch(Euser_abort & e) { quit = false; } } else quit = true; if(quit) throw; else { dialog->printf(gettext("re-enabling all signal handlers and continuing\n")); dar_suite_reset_signal_handler(); } } catch(Egeneric & e) { string tmp = e.get_message(); dialog->message(tools_printf(gettext("Error performing the requested action: %S"), &tmp)); } } while(choice != 'q'); if(arch != nullptr) throw SRC_BUG; } static void op_check(shared_ptr & dialog, const database *dat, bool info_details) { thread_cancellation thr; if(dat == nullptr) throw SRC_BUG; if(info_details) dialog->message(gettext("Checking date ordering of files between archives...")); if(!dat->check_order()) throw Edata(gettext("Some files do not follow chronological order when archive index increases withing the database, this can lead dar_manager to restored a wrong version of these files")); else dialog->message(gettext("No problem found")); thr.check_self_cancellation(); } static void op_batch(shared_ptr & dialog, database *dat, const string & filename, bool info_details) { const string pseudo_cmd = "dar_manager"; // to take the place of the command on the simulated command-line string line; char tmp; bool sub_info_details; deque mots; argc_argv cmdline; operation sub_op; string faked_base; archive_num num2; S_I num; vector rest; infinint date; string arg; bool ignore_dat_options; bool even_when_removed; bool check_order; // not used here compression algozip; // not used here neither but at the level of the function that called op_batch U_I compression_level; // not used here neither but at the level of the function that called op_batch bool change_compression; // not used here neither but at the level of the function that called op_batch shell_interaction *shelli = dynamic_cast(dialog.get()); if(shelli == nullptr) throw SRC_BUG; if(dat == nullptr) throw SRC_BUG; // openning the batch file if(info_details) dialog->message(gettext("Opening and reading the batch file...")); fichier_local batch_file = fichier_local(filename, false); no_comment proper = no_comment(batch_file); // removing the comments from file // reading it line by line and executing corresponding action try { do { line = ""; tmp = 'x'; // something different from '\n' for the end of current loop be properly detected // extracting the next line while(proper.read(&tmp, 1) == 1 && tmp != '\n') line += tmp; line_tools_split_in_words(line, mots); if(mots.size() == 0) continue; if(info_details) dialog->printf(gettext("\n\tExecuting batch file line: %S\n "), &line); cmdline.resize(mots.size() + 1); // +1 of pseudo_command cmdline.set_arg(pseudo_cmd, 0); for(U_I i = 0; i < mots.size(); ++i) cmdline.set_arg(mots[i], i+1); if(!command_line(*shelli, cmdline.argc(), cmdline.argv(), sub_op, faked_base, arg, num, rest, num2, date, sub_info_details, ignore_dat_options, even_when_removed, check_order, algozip, compression_level, change_compression, true)) throw Erange("op_batch", tools_printf(gettext("Syntax error in batch file: %S"), &line)); if(sub_op == create) throw Erange("op_batch", gettext("Syntax error in batch file: -C option not allowed")); if(sub_op == interactive) throw Erange("op_batch", gettext("Syntax error in batch file: -i option not allowed")); action(dialog, sub_op, dat, arg, num, rest, num2, date, faked_base, sub_info_details, false, ignore_dat_options, even_when_removed); } while(tmp == '\n'); } catch(Edata & e) { e.prepend_message(gettext("Aborting batch operation: ")); throw; } } static vector read_vector(shared_ptr & dialog) { vector ret; string tmp; ret.clear(); dialog->printf(gettext("Enter each argument line by line, press return at the end\n")); dialog->printf(gettext("To terminate enter an empty line\n")); do { tmp = dialog->get_string(" > ", true); if(tmp != "") ret.push_back(tmp); } while(tmp != ""); return ret; } static void finalize(shared_ptr & dialog, operation op, database *dat, const string & base, bool info_details, bool change_compression, compression new_compression, U_I compression_level) { switch(op) { case create: case listing: case restore: case used: case files: case stats: case interactive: case check: break; case add: case del: case chbase: case where: case options: case dar: case moving: case batch: if(info_details) dialog->message(gettext("Compressing and writing back database to file...")); write_base(dialog, base, dat, true, // overwriting change_compression, new_compression, compression_level); break; default: throw SRC_BUG; } } static void action(shared_ptr & dialog, operation op, database *dat, const string & arg, S_I num, const vector & rest, archive_num num2, const infinint & date, const string & base, bool info_details, bool early_release, bool ignore_database_options, bool even_when_removed) { shell_interaction *shelli = dynamic_cast(dialog.get()); if(shelli == nullptr) throw SRC_BUG; switch(op) { case none_op: throw SRC_BUG; case create: throw SRC_BUG; case add: op_add(dialog, dat, arg, rest.empty() ? "" : rest[0], date, info_details); break; case listing: op_listing(*shelli, dat, info_details); break; case del: op_del(dialog, dat, num, num2, info_details); break; case chbase: op_chbase(dialog, dat, num, arg, info_details); break; case where: op_where(dialog, dat, num, arg, info_details); break; case options: op_options(dialog, dat, rest, info_details); break; case dar: op_dar(dialog, dat, arg, info_details); break; case restore: op_restore(dialog, dat, rest, date, arg, info_details, early_release, ignore_database_options, even_when_removed); break; case used: op_used(*shelli, dat, num, info_details); break; case files: op_files(*shelli, dat, arg, info_details); break; case stats: op_stats(*shelli, dat, info_details); break; case moving: op_move(dialog, dat, num, num2, info_details); break; case interactive: op_interactive(dialog, dat, base); break; case check: op_check(dialog, dat, info_details); break; case batch: op_batch(dialog, dat, arg, info_details); break; default: throw SRC_BUG; } } static void signed_int_to_archive_num(S_I input, archive_num &num, bool & positive) { if(input < 0) { positive = false; input = -input; } else positive = true; if(input < 0) throw SRC_BUG; num = input; } dar-2.7.15/src/dar_suite/dar_xform.cpp0000644000175000017500000004006014636066467014552 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_UNISTD_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #include "getopt_decision.h" } // end extern "C" #include #include #include "dar_suite.hpp" #include "libdar.hpp" #include "line_tools.hpp" // includes directives that should be removed once dar_xform will be integrated into libdar #include "trivial_sar.hpp" #include "sar.hpp" #include "macro_tools.hpp" #include "libdar.hpp" // --- using namespace libdar; using namespace std; #define DAR_XFORM_VERSION "1.7.0" #define OPT_STRING "s:S:p::wnhbVE:F:a::Qj^:3:9:" static bool command_line(shell_interaction & dialog, S_I argc, char * const argv[], path * & src_dir, string & src, path * & dst_dir, string & dst, infinint & first_file_size, infinint & file_size, bool & warn, bool & allow, infinint & pause, bool & beep, string & execute_src, string & execute_dst, string & slice_perm, string & slice_user, string & slice_group, hash_algo & hash, infinint & src_min_digits, infinint & dst_min_digits); static void show_usage(shell_interaction & dialog, const char *command_name); static void show_version(shell_interaction & dialog, const char *command_name); static S_I sub_main(shared_ptr & dialog, S_I argc, char *const argv[], const char **env); int main(S_I argc, char *const argv[], const char **env) { return dar_suite_global(argc, argv, env, OPT_STRING, #if HAVE_GETOPT_LONG nullptr, #endif '\0', // should never be met as option, thus early read the whole command-line for -j and -Q options &sub_main); } static S_I sub_main(shared_ptr & dialog, S_I argc, char * const argv[], const char **env) { path *src_dir = nullptr; path *dst_dir = nullptr; string src, dst; infinint first, size; bool warn, allow, beep; infinint pause; string execute_src, execute_dst; thread_cancellation thr; string slice_perm; string slice_user; string slice_group; hash_algo hash; infinint src_min_digits; infinint dst_min_digits; S_I ret = EXIT_OK; shell_interaction *ptr = dynamic_cast(dialog.get()); std::unique_ptr xform; if(!dialog) throw SRC_BUG; if(ptr == nullptr) throw SRC_BUG; try { if(command_line(*ptr, argc, argv, src_dir, src, dst_dir, dst, first, size, warn, allow, pause, beep, execute_src, execute_dst, slice_perm, slice_user, slice_group, hash, src_min_digits, dst_min_digits)) { if(dst != "-") ptr->change_non_interactive_output(cout); ptr->set_beep(beep); if(src == "-") xform.reset(new (nothrow) libdar_xform(dialog, 0)); else xform.reset(new (nothrow) libdar_xform(dialog, src_dir->display(), src, EXTENSION, src_min_digits, execute_src)); if(!xform) throw Ememory("dar_xform"); if(dst == "-") xform->xform_to(1, execute_dst); else xform->xform_to(dst_dir->display(), dst, EXTENSION, allow, warn, pause, first, size, slice_perm, slice_user, slice_group, hash, dst_min_digits, execute_dst); ret = EXIT_OK; } else ret = EXIT_SYNTAX; } catch(...) { if(src_dir != nullptr) delete src_dir; if(dst_dir != nullptr) delete dst_dir; throw; } if(src_dir != nullptr) delete src_dir; if(dst_dir != nullptr) delete dst_dir; return ret; } static bool command_line(shell_interaction & dialog, S_I argc, char * const argv[], path * & src_dir, string & src, path * & dst_dir, string & dst, infinint & first_file_size, infinint & file_size, bool & warn, bool & allow, infinint & pause, bool & beep, string & execute_src, string & execute_dst, string & slice_perm, string & slice_user, string & slice_group, hash_algo & hash, infinint & src_min_digits, infinint & dst_min_digits) { S_I lu; src_dir = nullptr; dst_dir = nullptr; warn = true; allow = true; pause = 0; beep = false; first_file_size = 0; file_size = 0; src = dst = ""; execute_src = execute_dst = ""; U_I suffix_base = LINE_TOOLS_BIN_SUFFIX; slice_perm = ""; slice_user = ""; slice_group = ""; hash = hash_algo::none; src_min_digits = 0; dst_min_digits = 0; try { while((lu = getopt(argc, argv, OPT_STRING)) != EOF) { switch(lu) { case 's': if(!file_size.is_zero()) throw Erange("command_line", gettext("Only one -s option is allowed")); if(optarg == nullptr) throw Erange("command_line", gettext("Missing argument to -s")); else { try { file_size = tools_get_extended_size(optarg, suffix_base); if(first_file_size.is_zero()) first_file_size = file_size; } catch(Edeci &e) { dialog.message(gettext("Invalid size for option -s")); return false; } } break; case 'S': if(optarg == nullptr) throw Erange("command_line", gettext("Missing argument to -S")); if(first_file_size.is_zero()) first_file_size = tools_get_extended_size(optarg, suffix_base); else if(file_size.is_zero()) throw Erange("command_line", gettext("Only one -S option is allowed")); else if(file_size == first_file_size) { try { first_file_size = tools_get_extended_size(optarg, suffix_base); if(first_file_size == file_size) dialog.message(gettext("Giving -S option the same value as the one given to -s is useless")); } catch(Egeneric &e) { dialog.message(gettext("Invalid size for option -S")); return false; } } else throw Erange("command_line", gettext("Only one -S option is allowed")); break; case 'p': if(optarg != nullptr) { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci conv = string(optarg); pause = conv.computer(); } else pause = 1; break; case 'w': warn = false; break; case 'n': allow = false; break; case 'b': beep = true; break; case 'h': show_usage(dialog, argv[0]); return false; case 'V': show_version(dialog, argv[0]); return false; case 'E': if(optarg == nullptr) throw Erange("command_line", gettext("Missing argument to -E")); if(execute_dst == "") execute_dst = optarg; else execute_dst += string(" ; ") + optarg; break; case 'F': if(optarg == nullptr) throw Erange("command_line", gettext("Missing argument to -F")); if(execute_src == "") execute_src = optarg; else execute_src += string(" ; ") + optarg; break; case 'a': if(optarg == nullptr) throw Erange("command_line", gettext("-a option requires an argument")); if(strcasecmp("SI-unit", optarg) == 0 || strcasecmp("SI", optarg) == 0 || strcasecmp("SI-units", optarg) == 0) suffix_base = LINE_TOOLS_SI_SUFFIX; else if(strcasecmp("binary-unit", optarg) == 0 || strcasecmp("binary", optarg) == 0 || strcasecmp("binary-units", optarg) == 0) suffix_base = LINE_TOOLS_BIN_SUFFIX; else throw Erange("command_line", string(gettext("Unknown parameter given to -a option: ")) + optarg); break; case 'Q': break; // ignore this option already parsed during initialization (dar_suite.cpp) case '^': if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext("Missing argument to -^"), char(lu))); line_tools_slice_ownership(string(optarg), slice_perm, slice_user, slice_group); break; case '3': if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext("Missing argument to --hash"), char(lu))); if(strcasecmp(optarg, "md5") == 0) hash = hash_algo::md5; else if(strcasecmp(optarg, "sha1") == 0) hash = hash_algo::sha1; else throw Erange("command_line", string(gettext("Unknown parameter given to --hash option: ")) + optarg); break; case '9': if(optarg == nullptr) throw Erange("command_line", tools_printf(gettext("Missing argument to --min-digits"), char(lu))); else { infinint tmp2; line_tools_get_min_digits(optarg, src_min_digits, dst_min_digits, tmp2); } break; case ':': throw Erange("command_line", tools_printf(gettext("Missing parameter to option -%c"), char(optopt))); case '?': throw Erange("command_line", tools_printf(gettext("Ignoring unknown option -%c"), char(optopt))); default: throw SRC_BUG; } } // reading arguments remain on the command line if(optind + 2 > argc) { dialog.message(gettext("Missing source or destination argument on command line, see -h option for help")); return false; } if(optind + 2 < argc) { dialog.message(gettext("Too many argument on command line, see -h option for help")); return false; } if(string(argv[optind]) != string("")) { line_tools_split_path_basename(argv[optind], src_dir, src); line_tools_check_basename(dialog, *src_dir, src, EXTENSION, false); if(src_min_digits.is_zero()) line_tools_check_min_digits(dialog, *src_dir, src, EXTENSION, src_min_digits); } else { dialog.message(gettext("Invalid argument as source archive")); return false; } if(string(argv[optind+1]) != string("")) { line_tools_split_path_basename(argv[optind+1], dst_dir, dst); line_tools_check_basename(dialog, *dst_dir, dst, EXTENSION, true); } else { dialog.message(gettext("Invalid argument as destination archive")); return false; } // sanity checks if(dst == "-" && !file_size.is_zero()) throw Erange("dar_xform::command_line", gettext("Archive on stdout is not compatible with slicing (-s option)")); } catch(...) { if(src_dir != nullptr) delete src_dir; src_dir = nullptr; if(dst_dir != nullptr) delete dst_dir; dst_dir = nullptr; throw; } return true; } static void show_usage(shell_interaction & dialog, const char *command_name) { string name; line_tools_extract_basename(command_name, name); dialog.change_non_interactive_output(cout); dialog.printf("usage :\t %s [options] [/] [/]\n", name.c_str()); dialog.printf(" \t %s -h\n",name.c_str()); dialog.printf(" \t %s -V\n",name.c_str()); dialog.printf(gettext("\n")); dialog.printf(gettext("\t\t the first non options argument is the archive to read\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("\t\t the second non option argument is the archive to create\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("Common options:\n")); dialog.printf(gettext(" -h\t\t displays this help information\n")); dialog.printf(gettext(" -V\t\t displays version information\n")); dialog.printf(gettext(" -s split the archive in several files of size \n")); dialog.printf(gettext(" -S first file size\n")); dialog.printf(gettext(" -p\t\t pauses before writing to a new file\n")); dialog.printf(gettext(" -n\t\t don't overwrite files\n")); dialog.printf(gettext(" -w\t\t don't warn before overwriting files\n")); dialog.printf(gettext(" -b\t\t ring the terminal bell when user action is required\n")); dialog.printf(gettext(" -E \t command to execute between slices of destination archive\n")); dialog.printf(gettext(" -F \t command to execute between slice of source archive\n")); dialog.printf(gettext(" -aSI \t slice size suffixes k, M, T, G, etc. are power of 10\n")); dialog.printf(gettext(" -abinary\t slice size suffixes k, M, T, G, etc. are power of 2\n")); dialog.printf(gettext(" -^ \t permission[:user[:group]] of created slices\n")); dialog.printf(gettext("\n")); dialog.printf(gettext("See man page for more options.\n")); } static void show_version(shell_interaction & dialog, const char *command_name) { string name; line_tools_extract_basename(command_name, name); U_I maj, med, min; get_version(maj, med, min); dialog.change_non_interactive_output(cout); dialog.printf("\n %s version %s, Copyright (C) 2002-2024 Denis Corbin\n\n", name.c_str(), DAR_XFORM_VERSION); if(maj > 2) dialog.printf(gettext(" Using libdar %u.%u.%u built with compilation time options:\n"), maj, med, min); else dialog.printf(gettext(" Using libdar %u.%u built with compilation time options:\n"), maj, min); line_tools_display_features(dialog); dialog.printf("\n"); dialog.printf(gettext(" compiled the %s with %s version %s\n"), __DATE__, CC_NAT, __VERSION__); dialog.printf(gettext(" %s is part of the Disk ARchive suite (Release %s)\n"), name.c_str(), PACKAGE_VERSION); dialog.message(tools_printf(gettext(" %s comes with ABSOLUTELY NO WARRANTY; for details\n type `%s -W'."), name.c_str(), "dar") + tools_printf(gettext(" This is free software, and you are welcome\n to redistribute it under certain conditions;")) + tools_printf(gettext(" type `%s -L | more'\n for details.\n\n"), "dar")); } dar-2.7.15/src/dar_suite/command_line.hpp0000644000175000017500000003415014636067146015221 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file command_line.hpp /// \brief contains routing in charge of the command-line and included files parsing /// \ingroup CMDLINE #ifndef COMMAND_LINE_HPP #define COMMAND_LINE_HPP #include "../my_config.h" #include #include #include #include "libdar.hpp" using namespace std; using namespace libdar; /// \addtogroup CMDLINE /// @{ enum operation { noop, extract, create, diff, test, listing, isolate, merging, version_or_help, repairing }; // noop stands for no-operation. get_args() never returns such value, // it is just necessary within the command_line module enum dirty_behavior { dirtyb_ignore, dirtyb_warn, dirtyb_ok }; /// entrepot relative parameters struct ent_params { string ent_proto; ///< entrepot protocol string ent_login; ///< entrepot login secu_string ent_pass; ///< entrepot password string ent_host; ///< entrepot hostname string ent_port; ///< entrepot port U_I network_retry; ///< libcurl entrepot network retry time bool auth_from_file; ///< whether ~/.netrc and ~/.ssh files should be considered for credentials void clear() { ent_proto.clear(); ent_login.clear(); ent_pass.clear(); ent_host.clear(); ent_port.clear(); network_retry = 3; auth_from_file = false; }; }; /// all parameters retreived from command-line struct line_param { operation op; ///< which operation to perform path * fs_root; ///< filesystem root path * sauv_root; ///< where is the archive to operate on (create, read, etc.) string filename; ///< basename of the archive to operate on path * ref_root; ///< where is the archive of reference string * ref_filename; ///< basename of the archive of reference (nullptr => no archive of reference) infinint file_size; ///< size of the slices to create (except the first) infinint first_file_size; ///< sice of the first slice to create mask * selection; ///< filter files for the operation based on filename only mask * subtree; ///< filter files for the operation based on path+filename bool allow_over; ///< whether to allow slice overwriting bool warn_over; ///< whether to warn before overwriting files or slices bool info_details; ///< whether to show processing messages bool display_treated; ///< whether to show treated files bool display_treated_only_dir;///< whether to show treated files's current working directory bool display_skipped; ///< whether to display skipped files bool display_finished; ///< whether to display summary (space/compression ratio) for each completed directory bool display_masks; ///< whether to display masks value compression algo; ///< compression algorithm to use when generating an archive U_I compression_level; ///< compression level to use when generating an archive U_I compression_block_size; ///< compression block size (0 for legacy compression method) infinint pause; ///< whether to pause between slices bool beep; ///< whether to ring the terminal upon user interaction request bool empty_dir; ///< whether to store skipped directories as empty, whether to avoid restoring directory where no data is to be restored mask * ea_mask; ///< which EA to work on string input_pipe; ///< if not an empty string, name of the pipe through which to read data from dar_slave string output_pipe; ///< if not an empty string, name of the pipe through which to write orders to dar_slave comparison_fields what_to_check; ///< what fields to take into account when comparing/restoring files, string execute; ///< if not an empty string, the command to execute between slices string execute_ref; ///< if not an empty string, the command to execute between slices of the archive of reference secu_string pass; ///< if not an empty string, encrypt the archive with the given algo:pass string vector signatories; ///< list of email's key to use to sign the archive bool blind_signatures; ///< whether to ignore signature check failures secu_string pass_ref; ///< if not an empty string, use the provided encryption scheme to read the archive of reference mask * compress_mask; ///< which file to compress bool flat; ///< whether to ignore directory structure when restoring data infinint min_compr_size; ///< below which size to never try compressing files bool nodump; ///< whether to ignore files having the "nodump" flag set when performing a backup bool exclude_by_ea; ///< whether inode have to be check against a given EA before backup string ea_name_for_exclusion; ///< EA name to use for file exclusion, or empty string for the default EA name infinint hourshift; ///< consider equal two dates that have an integer hour of difference equal or less than hourshift bool warn_remove_no_match; ///< whether to warn file about to be removed during a restoration, when they to no match the expected type of file bool filter_unsaved; ///< whether to not list files that are not saved in the archive bool empty; ///< whether to do a dry-run execution bool alter_atime; ///< whether to reset the atime of file read during backup to their original value (resetting atime does modify ctime) bool same_fs; ///< whether to stick to a same filesystem deque same_fs_incl; ///< optional list of path to filesystems to stick to (for backup operation) deque same_fs_excl; ///< optional list of path to filesystems to not consider (for backup operation) bool snapshot; ///< whether to perform a snapshot backup bool cache_directory_tagging; ///< whether to ignore directory contents where a the cache directory tagging files is found U_32 crypto_size; ///< block size by which to cypher data U_32 crypto_size_ref; ///< block size by which to uncypher data from the archive of reference archive_options_listing_shell::listformat list_mode; ///< type of listing to follow path * aux_root; ///< where is the auxiliary archive of reference [used for merging but also when creating an archive, for the on-fly isolation] string * aux_filename; ///< basename of the auxiliary archive if reference (nullptr => no auxiliary of reference) secu_string aux_pass; ///< crypto to use for the auxiliary archive string aux_execute; ///< command to be run between the slice of the auxiliary archive of reference U_32 aux_crypto_size; ///< block size by which to cypher/uncypher data to/from the auxiliary archive of reference bool keep_compressed; ///< when merging, whether to not uncompress/re-compress data in the process infinint fixed_date; ///< the data for the snapshot backup bool quiet; ///< whether to display final summary for the operation const crit_action * overwrite;///< the overwriting policy string slice_perm; ///< permission to set when creating a slice string slice_user; ///< user to set when creating a slice string slice_group; ///< group to set when creating a slice infinint repeat_count; ///< number of time to try saving a file if it changes at the time it is read for backup infinint repeat_byte; ///< archive total maximum amount of byte to waste re-saving changing files bool decremental; ///< whether to produce a decremental backup (when merging) bool furtive_read_mode; ///< whether to use the furtive read mode bool lax; ///< whether to activate the last chance recovery mode (use with caution!) bool use_sequential_marks; ///< whether to add escape sequential marks in the archive bool sequential_read; ///< whether to follow escape sequential marks to achieve a sequential reading of the archive infinint sparse_file_min_size;///< minimum size of a zeroed byte sequence to be considered as a hole and stored this way in the archive dirty_behavior dirty; ///< what to do when comes the time to restore a file that is flagged as dirty bool security_check; ///< whether to signal possible root-kit presence string user_comment; ///< user comment to add to the archive hash_algo hash; ///< whether to produce a hash file, and which algoritm to use for that hash infinint num_digits; ///< minimum number of decimal for the slice number infinint ref_num_digits; ///< minimum number of decimal for the slice number of the archive of reference infinint aux_num_digits; ///< minimum number of decimal for the slice number of the auxiliary archive of reference bool only_deleted; ///< whether to only consider deleted files bool not_deleted; ///< whether to ignore deleted files mask * backup_hook_mask; ///< which file have to considered for backup hook string backup_hook_execute; ///< which command to execute as backup hook bool list_ea; ///< whether to list Extended Attribute of files bool ignore_unknown_inode; ///< whether to ignore unknown inode types bool no_compare_symlink_date; ///< whether to report difference in dates of symlinks while diffing an archive with filesystem fsa_scope scope; ///< FSA scope to consider for the operation U_I multi_threaded_crypto; ///< number of crypto worker threads (requires libthreadar) U_I multi_threaded_compress; ///< number of compress worker threads (requires libthreadar and per block compression) bool delta_sig; ///< whether to calculate rsync signature of files mask *delta_mask; ///< which file to calculate delta sig when not using the default mask bool delta_diff; ///< whether to save binary diff or whole file's data during a differential backup infinint delta_sig_min_size; ///< size below which to never calculate delta signatures ent_params remote; ///< remote entrepot coordinates ent_params ref_remote; ///< remote entrepot coordinates for archive of reference ent_params aux_remote; ///< remote entrepot coordinates for the auxiliary archive bool remote_verbose; ///< whether to have verbose output from libcurl bool sizes_in_bytes; ///< whether to display sizes in bytes of to the larges unit (Mo, Go, To,...) bool header_only; ///< whether we just display the header of archives to be read bool zeroing_neg_dates; ///< whether to automatically zeroing negative dates while reading inode from filesystem string ignored_as_symlink; ///< column separated list of absolute paths of links to follow rather to record as such modified_data_detection modet;///< how to detect that a file has changed since the archive of reference was done infinint iteration_count; ///< iteration count used when creating/isolating/merging an encrypted archive (key derivation) hash_algo kdf_hash; ///< hash algo used for key derivation function delta_sig_block_size delta_sig_len; ///< block len to used for delta signature computation bool unix_sockets; ///< whether to restore unix-sockets bool in_place; ///< in-place restoration of comparison // constructor for line_param line_param() { fs_root = nullptr; sauv_root = nullptr; ref_root = nullptr; selection = nullptr; subtree = nullptr; ref_filename = nullptr; ea_mask = nullptr; compress_mask = nullptr; aux_root = nullptr; aux_filename = nullptr; overwrite = nullptr; backup_hook_mask = nullptr; }; // destructor for line_param ~line_param() { if(fs_root != nullptr) delete fs_root; if(sauv_root != nullptr) delete sauv_root; if(ref_root != nullptr) delete ref_root; if(selection != nullptr) delete selection; if(subtree != nullptr) delete subtree; if(ref_filename != nullptr) delete ref_filename; if(ea_mask != nullptr) delete ea_mask; if(compress_mask != nullptr) delete compress_mask; if(aux_root != nullptr) delete aux_root; if(aux_filename != nullptr) delete aux_filename; if(overwrite != nullptr) delete overwrite; if(backup_hook_mask != nullptr) delete backup_hook_mask; }; }; /// main routine to extract parameters from command-line and included files extern bool get_args(shared_ptr & dialog, const char *home, const deque & dar_dcf_path, const deque & dar_duc_path, S_I argc, char * const argv[], line_param & param); #if HAVE_GETOPT_LONG const struct option *get_long_opt(); #endif const char *get_short_opt(); /// @} #endif dar-2.7.15/src/dar_suite/config_file.cpp0000644000175000017500000001046514636066467015043 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if STDC_HEADERS #include #endif } // end extern "C" #include "config_file.hpp" #include "tools.hpp" using namespace libdar; config_file::config_file(const deque & target, generic_file &f) : hide_file(f) { deque::const_iterator it = target.begin(); cibles.clear(); while(it != target.end()) { cibles.push_back(t_cible(*it)); ++it; } } deque config_file::get_read_targets() const { deque ret; deque::const_iterator it = cibles.begin(); while(it != cibles.end()) { if(it->seen) ret.push_back(it->target); ++it; } return ret; } void config_file::fill_morceau() { string read_target; partie tmp; infinint last_offset = 0; infinint first, last; enum { debut, fin } status = fin; // we read any text put before the first condition tmp.debut = 0; tmp.offset = 0; morceau.clear(); if(ref == nullptr) throw SRC_BUG; ref->skip(0); while(find_next_target(*ref, first, read_target, last)) { switch(status) { case fin: if(first < tmp.debut) throw SRC_BUG; // first byte of the line where next target // resides is before where we started looking for new target ! tmp.longueur = first - tmp.debut; last_offset += tmp.longueur; if(tmp.longueur > 0) morceau.push_back(tmp); status = debut; // no break ! case debut: if(is_a_target(read_target)) { tmp.debut = last; tmp.offset = last_offset; status = fin; } break; default: throw SRC_BUG; } } if(status == fin) { if(ref->get_position() < tmp.debut) throw SRC_BUG; tmp.longueur = ref->get_position() - tmp.debut; if(tmp.longueur > 0) morceau.push_back(tmp); } } bool config_file::is_a_target(const string & val) { deque::iterator it = cibles.begin(); while(it != cibles.end() && it->target != val) ++it; if(it != cibles.end()) { it->seen = true; return true; } else return false; } bool config_file::find_next_target(generic_file &f, infinint & debut, string & nature, infinint & fin) { char a; bool found = false; enum { init, search, end, purge } status = init; debut = f.get_position(); while(!found && f.read(&a, 1) == 1) { switch(status) { case init: // looking for next word beginning switch(a) { case ' ': case '\t': break; case '\n': debut = f.get_position(); break; default: if(isalpha(a)) { nature = a; status = search; } else status = purge; } break; case search: // reading the current word if(isalnum(a) || a == '_' || a == '-') nature += a; else if(a == ':') status = end; else if(a == '\n') { status = init; debut = f.get_position(); } else status = purge; break; case end: // got a target word (= word + `:' ) switch(a) { case ' ': case '\t': case '\r': break; case '\n': fin = f.get_position(); found = true; break; default: status = purge; } break; case purge: // just read word is not a target, resetting to the next line if(a == '\n') { status = init; debut = f.get_position(); } break; default: throw SRC_BUG; } } return found; } dar-2.7.15/src/dar_suite/crit_action_cmd_line.cpp0000644000175000017500000003265414636066467016733 00000000000000//*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include #include "crit_action_cmd_line.hpp" #include "tools.hpp" #include "line_tools.hpp" using namespace std; using namespace libdar; static const criterium * criterium_create_from_string(user_interaction & dialog, const string & argument, const infinint & hourshift); string crit_action_canonize_string(const std::string & argument) { string ret = ""; string::const_iterator it = argument.begin(); while(it != argument.end()) { if(*it != ' ' && *it != '\r' && *it != '\n' && *it != '\t') ret += *it; ++it; } return ret; } const crit_action * crit_action_create_from_string(user_interaction & dialog, const string & argument, const infinint & hourshift) { string::const_iterator it; const crit_action *ret = nullptr; if(argument.begin() == argument.end()) throw Erange("crit_action_create_from_string","Unexpected empty string in expression"); // looking for ';' (chain actions) try { it = line_tools_find_first_char_out_of_parenth(argument, ';'); if(it != argument.end()) { const crit_action *tmp = nullptr; const crit_chain *tmp_chain = nullptr; crit_chain *ret_chain = nullptr; try { if(*it != ';') throw SRC_BUG; ret = ret_chain = new (nothrow) crit_chain(); if(ret == nullptr) throw Ememory("crit_action_create_from_string"); tmp = crit_action_create_from_string(dialog, string(argument.begin(), it), hourshift); if(tmp == nullptr) throw SRC_BUG; ret_chain->add(*tmp); delete tmp; tmp = nullptr; tmp = crit_action_create_from_string(dialog, string(it+1, argument.end()), hourshift); tmp_chain = dynamic_cast(tmp); if(tmp_chain != nullptr) ret_chain->gobe(*(const_cast(tmp_chain))); else ret_chain->add(*tmp); delete tmp; tmp = nullptr; } catch(...) { if(tmp != nullptr) delete tmp; throw; } return ret; } if(*argument.begin() == '{') { string::const_iterator fin; const criterium *crit = nullptr; const crit_action *go_true = nullptr, *go_false = nullptr; it = line_tools_find_first_char_out_of_parenth(argument, '}'); if(it == argument.end()) throw Erange("crit_action_create_from_string", string(gettext("Missing } in conditional statement: ")) + argument); if(*it != '}') throw SRC_BUG; if(it + 1 == argument.end() || *(it + 1) != '[' ) throw Erange("crit_action_create_from_string", string(gettext("Missing [ after } in conditional statement: ")) + argument); fin = line_tools_find_first_char_out_of_parenth(argument, ']'); if(fin == argument.end()) throw Erange("crit_action_create_from_string", string(gettext("Missing ] in conditional statement: ")) + argument); if(*fin != ']') throw SRC_BUG; try { crit = criterium_create_from_string(dialog, string(argument.begin()+1, it), hourshift); if(crit == nullptr) throw SRC_BUG; go_true = crit_action_create_from_string(dialog, string(it + 2, fin), hourshift); if(go_true == nullptr) throw SRC_BUG; if(fin + 1 == argument.end()) { go_false = new (nothrow) crit_constant_action(data_undefined, EA_undefined); if(go_false == nullptr) throw Ememory("crit_action_create_from_string"); } else { go_false = crit_action_create_from_string(dialog, string(fin + 1, argument.end()), hourshift); if(go_false == nullptr) throw SRC_BUG; } ret = new (nothrow) testing(*crit, *go_true, *go_false); if(ret == nullptr) throw Ememory("crit_action_create_from_string"); delete crit; crit = nullptr; delete go_true; go_true = nullptr; delete go_false; go_false = nullptr; } catch(...) { if(crit != nullptr) delete crit; if(go_true != nullptr) delete go_true; if(go_false != nullptr) delete go_false; throw; } return ret; } if(argument.size() == 2) { over_action_data data; over_action_ea ea; switch(*argument.begin()) { case 'P': data = data_preserve; break; case 'O': data = data_overwrite; break; case 'S': data = data_preserve_mark_already_saved; break; case 'T': data = data_overwrite_mark_already_saved; break; case 'R': data = data_remove; break; case '*': data = data_undefined; break; case 'A': data = data_ask; break; default: throw Erange("crit_action_create_from_string", tools_printf(gettext("Unknown policy for data '%c' in expression %S"), *argument.begin(), &argument)); } switch(*(argument.begin() +1)) { case 'p': ea = EA_preserve; break; case 'o': ea = EA_overwrite; break; case 's': ea = EA_preserve_mark_already_saved; break; case 't': ea = EA_overwrite_mark_already_saved; break; case 'm': ea = EA_merge_preserve; break; case 'n': ea = EA_merge_overwrite; break; case 'r': ea = EA_clear; break; case '*': ea = EA_undefined; break; case 'a': ea = EA_ask; break; default: throw Erange("crit_action_create_from_string", tools_printf(gettext("Unknown policy for EA '%c' in expression %S"), *(argument.begin() +1), &argument)); } ret = new (nothrow) crit_constant_action(data, ea); if(ret == nullptr) throw Ememory("crit_action_create_from_string"); else return ret; } throw Erange("crit_action_create_from_string", string(gettext("Unknown expression in overwriting policy: ")) + argument); } catch(...) { if(ret != nullptr) delete ret; throw; } throw SRC_BUG; // we should never reach this statement } static const criterium * criterium_create_from_string(user_interaction &dialog, const string & argument, const infinint & hourshift) { string::const_iterator it; const criterium *ret = nullptr; if(argument.begin() == argument.end()) throw Erange("criterium_create_from_string","Unexpected empty string in expression"); try { // looking for '|' operator first it = line_tools_find_first_char_out_of_parenth(argument, '|'); if(it != argument.end()) { const criterium * tmp = nullptr; const crit_or *tmp_or = nullptr; crit_or *ret_or = nullptr; try { if(*it != '|') throw SRC_BUG; ret = ret_or = new (nothrow) crit_or(); if(ret == nullptr) throw Ememory("criterium_create_from_string"); tmp = criterium_create_from_string(dialog, string(argument.begin(), it), hourshift); if(tmp == nullptr) throw SRC_BUG; ret_or->add_crit(*tmp); delete tmp; tmp = nullptr; tmp = criterium_create_from_string(dialog, string(it+1, argument.end()), hourshift); tmp_or = dynamic_cast(tmp); if(tmp_or != nullptr) ret_or->gobe(*(const_cast(tmp_or))); else ret_or->add_crit(*tmp); delete tmp; tmp = nullptr; } catch(...) { if(tmp != nullptr) delete tmp; throw; } return ret; } // if no '|' is found, looking for '&' operator it = line_tools_find_first_char_out_of_parenth(argument, '&'); if(it != argument.end()) { const criterium *tmp = nullptr; const crit_and *tmp_and = nullptr; crit_and *ret_and = nullptr; try { if(*it != '&') throw SRC_BUG; ret = ret_and = new (nothrow) crit_and(); if(ret == nullptr) throw Ememory("criterium_create_from_string"); tmp = criterium_create_from_string(dialog, string(argument.begin(), it), hourshift); if(tmp == nullptr) throw SRC_BUG; ret_and->add_crit(*tmp); delete tmp; tmp = nullptr; tmp = criterium_create_from_string(dialog, string(it+1, argument.end()), hourshift); tmp_and = dynamic_cast(tmp); if(tmp_and != nullptr) ret_and->gobe(*(const_cast(tmp_and))); else ret_and->add_crit(*tmp); delete tmp; tmp = nullptr; } catch(...) { if(tmp != nullptr) delete tmp; throw; } return ret; } // else looking whether we have global parenthesis around the expresion if(*argument.begin() == '(' && *(argument.end() - 1) == ')') return criterium_create_from_string(dialog, string(argument.begin() + 1, argument.end() - 1), hourshift); else // well, this "else" statment is not necessary, I just found cleaner to add it to have a block in which to declare a temporary pointer { // else looking for unary operators const criterium *tmp = nullptr; try { switch(*argument.begin()) { case '!': tmp = criterium_create_from_string(dialog, string(argument.begin() + 1, argument.end()), hourshift); if(tmp == nullptr) throw SRC_BUG; ret = new (nothrow) crit_not(*tmp); delete tmp; tmp = nullptr; if(ret == nullptr) throw Ememory("criterium_create_from_string"); return ret; case '~': tmp = criterium_create_from_string(dialog, string(argument.begin() + 1, argument.end()), hourshift); if(tmp == nullptr) throw SRC_BUG; ret = new (nothrow) crit_invert(*tmp); delete tmp; tmp = nullptr; if(ret == nullptr) throw Ememory("criterium_create_from_string"); return ret; } } catch(...) { if(tmp != nullptr) delete tmp; throw; } } // else looking for atomic operator with argument if(argument.size() >= 4) // minimum size is "X(a)" thus 4 chars { if(*(argument.begin() + 1) == '(' && *(argument.end() - 1) == ')') { infinint date; string sub_arg = string(argument.begin() + 2, argument.end() - 1); // parsing and converting the argument of the atomic operator switch(*argument.begin()) { case 'R': case 'r': try { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci tmp = sub_arg; date = tmp.computer(); } catch(Edeci & e) { date = line_tools_convert_date(sub_arg); } break; default: throw Erange("criterium_create_from_string", string(gettext("Unknown atomic operator, or atomic not allowed with an argument: ") + argument)); } // creating the criterium with its parameter switch(*argument.begin()) { case 'R': ret = new (nothrow) crit_in_place_data_more_recent_or_equal_to(date, hourshift); break; case 'r': ret = new (nothrow) crit_in_place_EA_more_recent_or_equal_to(date, hourshift); break; default: throw SRC_BUG; } if(ret == nullptr) throw Ememory("criterium_create_from_string"); else return ret; } } // else looking for atomic operator if(argument.size() == 1) { switch(*argument.begin()) { case 'I': ret = new (nothrow) crit_in_place_is_inode(); break; case 'D': ret = new (nothrow) crit_in_place_is_dir(); break; case 'F': ret = new (nothrow) crit_in_place_is_file(); break; case 'H': ret = new (nothrow) crit_in_place_is_hardlinked_inode(); break; case 'A': ret = new (nothrow) crit_in_place_is_new_hardlinked_inode(); break; case 'R': ret = new (nothrow) crit_in_place_data_more_recent(hourshift); break; case 'B': ret = new (nothrow) crit_in_place_data_bigger(); break; case 'S': ret = new (nothrow) crit_in_place_data_saved(); break; case 'Y': ret = new (nothrow) crit_in_place_data_dirty(); break; case 'X': ret = new (nothrow) crit_in_place_data_sparse(); break; case 'L': ret = new (nothrow) crit_in_place_has_delta_sig(); break; case 'E': ret = new (nothrow) crit_same_inode_data(); break; case 'e': ret = new (nothrow) crit_in_place_EA_present(); break; case 'r': ret = new (nothrow) crit_in_place_EA_more_recent(hourshift); break; case 'm': ret = new (nothrow) crit_in_place_more_EA(); break; case 'b': ret = new (nothrow) crit_in_place_EA_bigger(); break; case 's': ret = new (nothrow) crit_in_place_EA_saved(); break; case 'T': ret = new (nothrow) crit_same_type(); break; default: throw Erange("criterium_create_from_string", string(gettext("Unknown character found while parsing conditional string: ")) + argument); } if(ret == nullptr) throw Ememory("criterium_create_from_string"); else return ret; } throw Erange("criterium_create_from_string", string(gettext("Unknown expression found while parsing conditional string: ")) + argument); } catch(...) { if(ret != nullptr) delete ret; throw; } throw SRC_BUG; // we should never reach this statement. } dar-2.7.15/src/dar_suite/dar.cpp0000644000175000017500000014372714636067146013350 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" #include #include #include #include #include "libdar.hpp" #include "shell_interaction.hpp" #include "dar_suite.hpp" #include "command_line.hpp" #include "line_tools.hpp" #ifndef DAR_VERSION #define DAR_VERSION "unknown (BUG at compilation time?)" #endif using namespace std; using namespace libdar; static void display_sauv_stat(user_interaction & dialog, const statistics & st); static void display_rest_stat(user_interaction & dialog, const statistics & st); static void display_diff_stat(user_interaction & dialog, const statistics &st); static void display_test_stat(user_interaction & dialog, const statistics & st); static void display_merge_stat(user_interaction & dialog, const statistics & st); static S_I little_main(shared_ptr & dialog, S_I argc, char * const argv[], const char **env); int main(S_I argc, char * const argv[], const char **env) { return dar_suite_global(argc, argv, env, get_short_opt(), #if HAVE_GETOPT_LONG get_long_opt(), #endif '\0', // should never be met as option, thus early read the whole command-line for -j and -Q options &little_main); } static S_I little_main(shared_ptr & dialog, S_I argc, char * const argv[], const char **env) { S_I ret = EXIT_OK; line_param param; const char *home = line_tools_get_from_env(env, "HOME"); deque dar_dcf_path = line_tools_explode_PATH(line_tools_get_from_env(env, "DAR_DCF_PATH")); deque dar_duc_path = line_tools_explode_PATH(line_tools_get_from_env(env, "DAR_DUC_PATH")); string sftp_known_hosts; string sftp_pub_filekey; string sftp_prv_filekey; const char *env_knownhosts = line_tools_get_from_env(env, "DAR_SFTP_KNOWNHOSTS_FILE"); const char *env_pub_filekey = line_tools_get_from_env(env, "DAR_SFTP_PUBLIC_KEYFILE"); const char *env_prv_filekey = line_tools_get_from_env(env, "DAR_SFTP_PRIVATE_KEYFILE"); const char *env_ignored_as_symlink = line_tools_get_from_env(env, "DAR_IGNORED_AS_SYMLINK"); shell_interaction *shelli = dynamic_cast(dialog.get()); string home_pref; if(!dialog) throw SRC_BUG; if(shelli == nullptr) throw SRC_BUG; if(home == nullptr) { home = "/"; home_pref = ""; } else home_pref = string(home); if(env_knownhosts != nullptr) sftp_known_hosts = string(env_knownhosts); else sftp_known_hosts = home_pref + "/.ssh/known_hosts"; if(env_pub_filekey != nullptr) sftp_pub_filekey = string(env_pub_filekey); else sftp_pub_filekey = home_pref + "/.ssh/id_rsa.pub"; if(env_prv_filekey != nullptr) sftp_prv_filekey = string(env_prv_filekey); else sftp_prv_filekey = home_pref + "/.ssh/id_rsa"; if(! get_args(dialog, home, dar_dcf_path, dar_duc_path, argc, argv, param)) { if(param.op == version_or_help) return EXIT_OK; else return EXIT_SYNTAX; } else // get_args is OK, we've got a valid command line { shared_ptr repo; shared_ptr ref_repo; shared_ptr aux_repo; shell_interaction *ptr = dynamic_cast(dialog.get()); if(ptr != nullptr) ptr->set_beep(param.beep); if(param.display_masks) { const string initial_prefix = " "; if(param.subtree != nullptr) { string res = param.subtree->dump(initial_prefix); dialog->message("directory tree filter:"); dialog->message(res); dialog->message(""); } if(param.selection != nullptr) { string res = param.selection->dump(initial_prefix); dialog->message("filename filter:"); dialog->message(res); dialog->message(""); } if(param.ea_mask != nullptr) { string res = param.ea_mask->dump(initial_prefix); dialog->message("EA filter:"); dialog->message(res); dialog->message(""); } if(param.compress_mask != nullptr) { string res = param.compress_mask->dump(initial_prefix); dialog->message("Compression filter:"); dialog->message(res); dialog->message(""); } if(param.backup_hook_mask != nullptr) { string res = param.backup_hook_mask->dump(initial_prefix); dialog->message("backup hook filter:"); dialog->message(res); dialog->message(""); } } if(param.filename != "-" || (param.output_pipe != "" && param.op != create && param.op != isolate && param.op != merging)) { shell_interaction *ptr = dynamic_cast(dialog.get()); if(ptr != nullptr) ptr->change_non_interactive_output(cout); } // standart output can be used to send non interactive // messages try { shared_ptr arch; shared_ptr aux; unique_ptr cur; statistics st = false; secu_string tmp_pass; crypto_algo crypto, aux_crypto; archive_options_read read_options; archive_options_create create_options; archive_options_isolate isolate_options; archive_options_merge merge_options; archive_options_extract extract_options; archive_options_listing_shell listing_options; archive_options_diff diff_options; archive_options_test test_options; archive_options_repair repair_options; bool no_cipher_given; vector recipients; set ignored_as_symlink_listing; if(param.remote.ent_host.size() != 0) { repo.reset(new (nothrow) entrepot_libcurl(dialog, string_to_mycurl_protocol(param.remote.ent_proto), param.remote.ent_login, param.remote.ent_pass, param.remote.ent_host, param.remote.ent_port, param.remote.auth_from_file, sftp_pub_filekey, sftp_prv_filekey, sftp_known_hosts, param.remote.network_retry, param.remote_verbose)); if(!repo) throw Ememory("little_main"); } if(param.ref_remote.ent_host.size() != 0) { ref_repo.reset(new (nothrow) entrepot_libcurl(dialog, string_to_mycurl_protocol(param.ref_remote.ent_proto), param.ref_remote.ent_login, param.ref_remote.ent_pass, param.ref_remote.ent_host, param.ref_remote.ent_port, param.ref_remote.auth_from_file, sftp_pub_filekey, sftp_prv_filekey, sftp_known_hosts, param.ref_remote.network_retry, param.remote_verbose)); if(!ref_repo) throw Ememory("little_main"); } if(param.aux_remote.ent_host.size() != 0) { aux_repo.reset(new (nothrow) entrepot_libcurl(dialog, string_to_mycurl_protocol(param.aux_remote.ent_proto), param.aux_remote.ent_login, param.aux_remote.ent_pass, param.aux_remote.ent_host, param.aux_remote.ent_port, param.aux_remote.auth_from_file, sftp_pub_filekey, sftp_prv_filekey, sftp_known_hosts, param.aux_remote.network_retry, param.remote_verbose)); if(!aux_repo) throw Ememory("little_main"); } switch(param.op) { case create: case merging: case repairing: if(param.ref_filename != nullptr && param.ref_root != nullptr) { line_tools_crypto_split_algo_pass(param.pass_ref, crypto, tmp_pass, no_cipher_given, recipients); if(param.op == merging && param.aux_root != nullptr && param.info_details) dialog->message(gettext("Considering the (first) archive of reference:")); if(param.sequential_read && param.delta_diff) throw Erange("little_main",gettext("Sequential reading of the archive of reference is not possible when delta difference is requested, you need either to read the archive of reference in direct access mode (without \'--sequential-read\' option) or disable binary delta (adding \'--delta no-patch\' option)")); read_options.clear(); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_crypto_algo(crypto_algo::none); else read_options.set_crypto_algo(crypto); read_options.set_crypto_pass(tmp_pass); read_options.set_crypto_size(param.crypto_size_ref); read_options.set_input_pipe(param.input_pipe); read_options.set_output_pipe(param.output_pipe); read_options.set_execute(param.execute_ref); read_options.set_info_details(param.info_details); read_options.set_lax(param.lax); read_options.set_slice_min_digits(param.ref_num_digits); read_options.set_ignore_signature_check_failure(param.blind_signatures); read_options.set_multi_threaded_crypto(param.multi_threaded_crypto); read_options.set_multi_threaded_compress(param.multi_threaded_compress); if(param.sequential_read) { if(param.op == merging) throw Erange("little_main", gettext("Using sequential reading mode for archive source is not possible for merging operation")); else read_options.set_sequential_read(true); } if(ref_repo) read_options.set_entrepot(ref_repo); if(param.op != repairing) { arch.reset(new (nothrow) archive(dialog, *param.ref_root, *param.ref_filename, EXTENSION, read_options)); if(!arch) throw Ememory("little_main"); } else // repairing arch.reset(); } if(param.aux_root != nullptr && param.aux_filename != nullptr) { if(param.op != merging && param.op != create) throw SRC_BUG; if(param.op == merging) { if(param.info_details) dialog->message(gettext("Considering the second (alias auxiliary) archive of reference:")); line_tools_crypto_split_algo_pass(param.aux_pass, aux_crypto, tmp_pass, no_cipher_given, recipients); read_options.clear(); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_crypto_algo(crypto_algo::none); else read_options.set_crypto_algo(aux_crypto); read_options.set_crypto_pass(tmp_pass); read_options.set_crypto_size(param.aux_crypto_size); read_options.set_execute(param.aux_execute); read_options.set_info_details(param.info_details); read_options.set_lax(param.lax); read_options.set_slice_min_digits(param.aux_num_digits); read_options.set_ignore_signature_check_failure(param.blind_signatures); read_options.set_multi_threaded_crypto(param.multi_threaded_crypto); read_options.set_multi_threaded_compress(param.multi_threaded_compress); if(param.sequential_read) throw Erange("little_main", gettext("Using sequential reading mode for archive source is not possible for merging operation")); if(aux_repo) read_options.set_entrepot(aux_repo); aux.reset(new (nothrow) archive(dialog, *param.aux_root, *param.aux_filename, EXTENSION, read_options)); if(!aux) throw Ememory("little_main"); } } line_tools_crypto_split_algo_pass(param.pass, crypto, tmp_pass, no_cipher_given, recipients); switch(param.op) { case create: create_options.clear(); if(arch) { if(!param.delta_sig && !param.delta_diff) // we may need to copy delta_sig // from the archive of reference // to the resulting archive so we // cannot drop fd when delta_sig // is requested // We may also need to read delta_sig // to perform the delta difference arch->drop_all_filedescriptors(); create_options.set_reference(arch); } create_options.set_selection(*param.selection); create_options.set_subtree(*param.subtree); create_options.set_allow_over(param.allow_over); create_options.set_warn_over(param.warn_over); create_options.set_info_details(param.info_details); create_options.set_display_treated(param.display_treated, param.display_treated_only_dir); create_options.set_display_skipped(param.display_skipped); create_options.set_display_finished(param.display_finished); create_options.set_pause(param.pause); create_options.set_empty_dir(param.empty_dir); create_options.set_compression(param.algo); create_options.set_compression_level(param.compression_level); create_options.set_compression_block_size(param.compression_block_size); create_options.set_slicing(param.file_size, param.first_file_size); create_options.set_ea_mask(*param.ea_mask); create_options.set_execute(param.execute); create_options.set_crypto_algo(crypto); create_options.set_crypto_pass(tmp_pass); create_options.set_crypto_size(param.crypto_size); create_options.set_gnupg_recipients(recipients); if(recipients.empty() && !param.signatories.empty()) throw Erange("little_main", gettext("Archive signature is only possible with gnupg encryption")); create_options.set_gnupg_signatories(param.signatories); create_options.set_compr_mask(*param.compress_mask); create_options.set_min_compr_size(param.min_compr_size); create_options.set_nodump(param.nodump); if(param.exclude_by_ea) create_options.set_exclude_by_ea(param.ea_name_for_exclusion); create_options.set_what_to_check(param.what_to_check); create_options.set_hourshift(param.hourshift); create_options.set_empty(param.empty); create_options.set_alter_atime(param.alter_atime); create_options.set_furtive_read_mode(param.furtive_read_mode); if(param.same_fs_incl.empty() && param.same_fs_excl.empty()) // legacy -M option create_options.set_same_fs(param.same_fs); else // new 2.7.0 -M option syntax { deque::iterator it = param.same_fs_incl.begin(); while(it != param.same_fs_incl.end()) { create_options.set_same_fs_include(*it); ++it; } it = param.same_fs_excl.begin(); while(it != param.same_fs_excl.end()) { create_options.set_same_fs_exclude(*it); ++it; } } create_options.set_snapshot(param.snapshot); create_options.set_cache_directory_tagging(param.cache_directory_tagging); create_options.set_fixed_date(param.fixed_date); create_options.set_slice_permission(param.slice_perm); create_options.set_slice_user_ownership(param.slice_user); create_options.set_slice_group_ownership(param.slice_group); create_options.set_retry_on_change(param.repeat_count, param.repeat_byte); create_options.set_sequential_marks(param.use_sequential_marks); create_options.set_sparse_file_min_size(param.sparse_file_min_size); create_options.set_security_check(param.security_check); create_options.set_user_comment(param.user_comment); create_options.set_hash_algo(param.hash); create_options.set_slice_min_digits(param.num_digits); create_options.set_fsa_scope(param.scope); create_options.set_multi_threaded_crypto(param.multi_threaded_crypto); create_options.set_multi_threaded_compress(param.multi_threaded_compress); create_options.set_delta_signature(param.delta_sig); if(param.delta_sig_min_size > 0) create_options.set_delta_sig_min_size(param.delta_sig_min_size); if(compile_time::librsync()) create_options.set_delta_diff(param.delta_diff); create_options.set_auto_zeroing_neg_dates(param.zeroing_neg_dates); if(param.backup_hook_mask != nullptr) create_options.set_backup_hook(param.backup_hook_execute, *param.backup_hook_mask); create_options.set_ignore_unknown_inode_type(param.ignore_unknown_inode); if(param.delta_mask != nullptr) create_options.set_delta_mask(*param.delta_mask); if(repo) create_options.set_entrepot(repo); if(param.ignored_as_symlink.size() > 0) { deque tmp; line_tools_split(param.ignored_as_symlink, ':', tmp); ignored_as_symlink_listing = line_tools_deque_to_set(tmp); } else { if(env_ignored_as_symlink != nullptr) { deque tmp; line_tools_split(env_ignored_as_symlink, ':', tmp); ignored_as_symlink_listing = line_tools_deque_to_set(tmp); } } create_options.set_ignored_as_symlink(ignored_as_symlink_listing); create_options.set_modified_data_detection(param.modet); if(param.iteration_count > 0) create_options.set_iteration_count(param.iteration_count); if(param.kdf_hash != hash_algo::none) create_options.set_kdf_hash(param.kdf_hash); create_options.set_sig_block_len(param.delta_sig_len); cur.reset(new (nothrow) archive(dialog, *param.fs_root, *param.sauv_root, param.filename, EXTENSION, create_options, &st)); if(!cur) throw Ememory("little_main"); if(!param.quiet) display_sauv_stat(*dialog, st); break; case merging: merge_options.clear(); merge_options.set_auxiliary_ref(aux); merge_options.set_selection(*param.selection); merge_options.set_subtree(*param.subtree); merge_options.set_allow_over(param.allow_over); merge_options.set_overwriting_rules(*param.overwrite); merge_options.set_warn_over(param.warn_over); merge_options.set_info_details(param.info_details); merge_options.set_display_treated(param.display_treated, param.display_treated_only_dir); merge_options.set_display_skipped(param.display_skipped); merge_options.set_pause(param.pause); merge_options.set_empty_dir(param.empty_dir); merge_options.set_compression(param.algo); merge_options.set_compression_level(param.compression_level); merge_options.set_compression_block_size(param.compression_block_size); merge_options.set_slicing(param.file_size, param.first_file_size); merge_options.set_ea_mask(*param.ea_mask); merge_options.set_execute(param.execute); merge_options.set_crypto_algo(crypto); merge_options.set_crypto_pass(tmp_pass); merge_options.set_crypto_size(param.crypto_size); merge_options.set_gnupg_recipients(recipients); if(recipients.empty() && !param.signatories.empty()) throw Erange("little_main", gettext("Archive signature is only possible with gnupg encryption")); merge_options.set_gnupg_signatories(param.signatories); merge_options.set_compr_mask(*param.compress_mask); merge_options.set_min_compr_size(param.min_compr_size); merge_options.set_empty(param.empty); merge_options.set_keep_compressed(param.keep_compressed); merge_options.set_slice_permission(param.slice_perm); merge_options.set_slice_user_ownership(param.slice_user); merge_options.set_slice_group_ownership(param.slice_group); merge_options.set_decremental_mode(param.decremental); merge_options.set_sequential_marks(param.use_sequential_marks); merge_options.set_sparse_file_min_size(param.sparse_file_min_size); merge_options.set_user_comment(param.user_comment); merge_options.set_hash_algo(param.hash); merge_options.set_slice_min_digits(param.num_digits); merge_options.set_fsa_scope(param.scope); merge_options.set_multi_threaded_crypto(param.multi_threaded_crypto); merge_options.set_multi_threaded_compress(param.multi_threaded_compress); merge_options.set_delta_signature(param.delta_sig); if(param.delta_mask != nullptr) merge_options.set_delta_mask(*param.delta_mask); if(param.delta_sig_min_size > 0) merge_options.set_delta_sig_min_size(param.delta_sig_min_size); if(repo) merge_options.set_entrepot(repo); if(param.iteration_count > 0) merge_options.set_iteration_count(param.iteration_count); if(param.kdf_hash != hash_algo::none) merge_options.set_kdf_hash(param.kdf_hash); merge_options.set_sig_block_len(param.delta_sig_len); cur.reset(new (nothrow) archive(dialog, // user_interaction & *param.sauv_root, //const path & arch, // the mandatory archive of reference param.filename, // const string & EXTENSION, // const string & merge_options, &st)); // statistics* if(!cur) throw Ememory("little_main"); if(!param.quiet) display_merge_stat(*dialog, st); break; case repairing: repair_options.clear(); repair_options.set_warn_over(param.warn_over); repair_options.set_allow_over(param.allow_over); repair_options.set_info_details(param.info_details); repair_options.set_display_treated(param.display_treated, param.display_treated_only_dir); repair_options.set_display_skipped(param.display_skipped); repair_options.set_pause(param.pause); repair_options.set_slicing(param.file_size, param.first_file_size); repair_options.set_execute(param.execute); repair_options.set_crypto_algo(crypto); repair_options.set_crypto_pass(tmp_pass); repair_options.set_crypto_size(param.crypto_size); repair_options.set_gnupg_recipients(recipients); if(recipients.empty() && !param.signatories.empty()) throw Erange("little_main", gettext("Archive signature is only possible with gnupg encryption")); repair_options.set_gnupg_signatories(param.signatories); repair_options.set_empty(param.empty); repair_options.set_slice_permission(param.slice_perm); repair_options.set_slice_user_ownership(param.slice_user); repair_options.set_slice_group_ownership(param.slice_group); repair_options.set_user_comment(param.user_comment); repair_options.set_hash_algo(param.hash); repair_options.set_slice_min_digits(param.num_digits); repair_options.set_multi_threaded_crypto(param.multi_threaded_crypto); repair_options.set_multi_threaded_compress(param.multi_threaded_compress); if(repo) repair_options.set_entrepot(repo); cur.reset(new (nothrow) archive(dialog, *param.ref_root, *param.ref_filename, EXTENSION, read_options, *param.sauv_root, param.filename, EXTENSION, repair_options)); if(!cur) throw Ememory("little_main"); break; default: throw SRC_BUG; } // making some room in memory if(param.info_details) dialog->message(gettext("Making room in memory (releasing memory used by archive of reference)...")); arch.reset(); aux.reset(); // checking for onfly isolation if(!st.get_errored().is_zero()) ret = EXIT_DATA_ERROR; if(!st.get_tooold().is_zero()) ret = EXIT_SAVED_MODIFIED; if(param.op == create) if(param.aux_root != nullptr && param.aux_filename != nullptr) { if(param.op != merging && param.op != create) throw SRC_BUG; if(param.op == create) { if(!param.quiet) dialog->message(gettext("Now performing on-fly isolation...")); if(cur == nullptr) throw SRC_BUG; line_tools_crypto_split_algo_pass(param.aux_pass, aux_crypto, tmp_pass, no_cipher_given, recipients); isolate_options.clear(); isolate_options.set_allow_over(param.allow_over); isolate_options.set_warn_over(param.warn_over); isolate_options.set_info_details(param.info_details); isolate_options.set_pause(param.pause); if(compile_time::libbz2()) isolate_options.set_compression(compression::bzip2); else if(compile_time::libz()) isolate_options.set_compression(compression::gzip); else if(compile_time::liblzo()) isolate_options.set_compression(compression::lzo); else // no compression isolate_options.set_compression(compression::none); isolate_options.set_execute(param.aux_execute); isolate_options.set_crypto_algo(aux_crypto); isolate_options.set_crypto_pass(tmp_pass); isolate_options.set_crypto_size(param.aux_crypto_size); isolate_options.set_gnupg_recipients(recipients); if(recipients.empty() && !param.signatories.empty()) throw Erange("little_main", gettext("Archive signature is only possible with gnupg encryption")); isolate_options.set_gnupg_signatories(param.signatories); isolate_options.set_empty(param.empty); isolate_options.set_slice_permission(param.slice_perm); isolate_options.set_slice_user_ownership(param.slice_user); isolate_options.set_slice_group_ownership(param.slice_group); isolate_options.set_hash_algo(param.hash); isolate_options.set_slice_min_digits(param.aux_num_digits); isolate_options.set_user_comment(param.user_comment); isolate_options.set_sequential_marks(param.use_sequential_marks); isolate_options.set_multi_threaded_crypto(param.multi_threaded_crypto); isolate_options.set_multi_threaded_compress(param.multi_threaded_compress); // copying delta sig is not possible in on-fly isolation, // archive must be closed and re-open in read mode to be able // to fetch delta signatures isolate_options.set_delta_signature(false); if(aux_repo) isolate_options.set_entrepot(aux_repo); if(param.iteration_count > 0) isolate_options.set_iteration_count(param.iteration_count); if(param.kdf_hash != hash_algo::none) isolate_options.set_kdf_hash(param.kdf_hash); isolate_options.set_sig_block_len(param.delta_sig_len); cur->op_isolate(*param.aux_root, *param.aux_filename, EXTENSION, isolate_options); } } break; case isolate: line_tools_crypto_split_algo_pass(param.pass_ref, crypto, tmp_pass, no_cipher_given, recipients); read_options.clear(); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_crypto_algo(crypto_algo::none); else read_options.set_crypto_algo(crypto); read_options.set_crypto_pass(tmp_pass); read_options.set_crypto_size(param.crypto_size_ref); read_options.set_input_pipe(param.input_pipe); read_options.set_output_pipe(param.output_pipe); read_options.set_execute(param.execute_ref); read_options.set_info_details(param.info_details); read_options.set_lax(param.lax); read_options.set_sequential_read(param.sequential_read); read_options.set_slice_min_digits(param.ref_num_digits); read_options.set_ignore_signature_check_failure(param.blind_signatures); read_options.set_multi_threaded_crypto(param.multi_threaded_crypto); read_options.set_multi_threaded_compress(param.multi_threaded_compress); if(ref_repo) read_options.set_entrepot(ref_repo); // yes this is "ref_repo" where is located the -A-pointed-to archive // -C-pointed-to archive is located in the "repo" entrepot arch.reset(new (nothrow) archive(dialog, *param.ref_root, *param.ref_filename, EXTENSION, read_options)); if(!arch) throw Ememory("little_main"); if(!param.delta_sig) arch->drop_all_filedescriptors(); line_tools_crypto_split_algo_pass(param.pass, crypto, tmp_pass, no_cipher_given, recipients); isolate_options.clear(); isolate_options.set_allow_over(param.allow_over); isolate_options.set_warn_over(param.warn_over); isolate_options.set_info_details(param.info_details); isolate_options.set_pause(param.pause); isolate_options.set_compression(param.algo); isolate_options.set_compression_level(param.compression_level); isolate_options.set_compression_block_size(param.compression_block_size); isolate_options.set_slicing(param.file_size, param.first_file_size); isolate_options.set_execute(param.execute); isolate_options.set_crypto_algo(crypto); isolate_options.set_crypto_pass(tmp_pass); isolate_options.set_crypto_size(param.crypto_size); isolate_options.set_gnupg_recipients(recipients); if(recipients.empty() && !param.signatories.empty()) throw Erange("little_main", gettext("Archive signature is only possible with gnupg encryption")); isolate_options.set_gnupg_signatories(param.signatories); isolate_options.set_empty(param.empty); isolate_options.set_slice_permission(param.slice_perm); isolate_options.set_slice_user_ownership(param.slice_user); isolate_options.set_slice_group_ownership(param.slice_group); isolate_options.set_user_comment(param.user_comment); isolate_options.set_hash_algo(param.hash); isolate_options.set_slice_min_digits(param.num_digits); isolate_options.set_sequential_marks(param.use_sequential_marks); isolate_options.set_multi_threaded_crypto(param.multi_threaded_crypto); isolate_options.set_multi_threaded_compress(param.multi_threaded_compress); isolate_options.set_delta_signature(param.delta_sig); if(param.delta_mask != nullptr) isolate_options.set_delta_mask(*param.delta_mask); if(param.delta_sig_min_size > 0) isolate_options.set_delta_sig_min_size(param.delta_sig_min_size); if(repo) isolate_options.set_entrepot(repo); // yes this is "ref_repo" where is located the -A-pointed-to archive // -C-pointed-to archive is located in the "repo" entrepot if(param.iteration_count > 0) isolate_options.set_iteration_count(param.iteration_count); if(param.kdf_hash != hash_algo::none) isolate_options.set_kdf_hash(param.kdf_hash); isolate_options.set_sig_block_len(param.delta_sig_len); arch->op_isolate(*param.sauv_root, param.filename, EXTENSION, isolate_options); break; case extract: line_tools_crypto_split_algo_pass(param.pass, crypto, tmp_pass, no_cipher_given, recipients); read_options.clear(); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_crypto_algo(crypto_algo::none); else read_options.set_crypto_algo(crypto); read_options.set_crypto_pass(tmp_pass); read_options.set_crypto_size(param.crypto_size); read_options.set_input_pipe(param.input_pipe); read_options.set_output_pipe(param.output_pipe); read_options.set_execute(param.execute); read_options.set_info_details(param.info_details); read_options.set_lax(param.lax); read_options.set_sequential_read(param.sequential_read); read_options.set_slice_min_digits(param.num_digits); read_options.set_ignore_signature_check_failure(param.blind_signatures); read_options.set_multi_threaded_crypto(param.multi_threaded_crypto); read_options.set_multi_threaded_compress(param.multi_threaded_compress); if(repo) read_options.set_entrepot(repo); if(param.ref_filename != nullptr && param.ref_root != nullptr) { secu_string ref_tmp_pass; crypto_algo ref_crypto; line_tools_crypto_split_algo_pass(param.pass_ref, ref_crypto, ref_tmp_pass, no_cipher_given, recipients); read_options.set_external_catalogue(*param.ref_root, *param.ref_filename); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_ref_crypto_algo(crypto_algo::none); else read_options.set_ref_crypto_algo(ref_crypto); read_options.set_ref_crypto_pass(ref_tmp_pass); read_options.set_ref_crypto_size(param.crypto_size_ref); read_options.set_ref_execute(param.execute_ref); read_options.set_ref_slice_min_digits(param.ref_num_digits); if(ref_repo) read_options.set_ref_entrepot(ref_repo); } arch.reset(new (nothrow) archive(dialog, *param.sauv_root, param.filename, EXTENSION, read_options)); if(!arch) throw Ememory("little_main"); extract_options.clear(); extract_options.set_selection(*param.selection); extract_options.set_subtree(*param.subtree); extract_options.set_warn_over(param.warn_over); extract_options.set_info_details(param.info_details); extract_options.set_display_treated(param.display_treated, param.display_treated_only_dir); extract_options.set_display_skipped(param.display_skipped); extract_options.set_ea_mask(*param.ea_mask); extract_options.set_flat(param.flat); extract_options.set_what_to_check(param.what_to_check); extract_options.set_warn_remove_no_match(param.warn_remove_no_match); extract_options.set_empty(param.empty); extract_options.set_empty_dir(!param.empty_dir); // the inversion above (!) is due to the fact this option is // set with the same -D option that is used at backup time to // save as empty directories those which have been excluded // by filters, option, which default is 'false', but here // in a restoration context, unless -D is provided (which set the // option to "true"), we want that all directories, empty or not, be restored extract_options.set_overwriting_rules(*param.overwrite); switch(param.dirty) { case dirtyb_ignore: extract_options.set_dirty_behavior(true, false); break; case dirtyb_warn: extract_options.set_dirty_behavior(false, true); break; case dirtyb_ok: extract_options.set_dirty_behavior(false, false); break; default: throw SRC_BUG; } extract_options.set_only_deleted(param.only_deleted); extract_options.set_ignore_deleted(param.not_deleted); extract_options.set_fsa_scope(param.scope); extract_options.set_ignore_unix_sockets(param.unix_sockets); extract_options.set_in_place(param.in_place); st = arch->op_extract(*param.fs_root, extract_options, nullptr); if(!param.quiet) display_rest_stat(*dialog, st); if(st.get_errored() > 0) throw Edata(gettext("All files asked could not be restored")); break; case diff: line_tools_crypto_split_algo_pass(param.pass, crypto, tmp_pass, no_cipher_given, recipients); read_options.clear(); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_crypto_algo(crypto_algo::none); else read_options.set_crypto_algo(crypto); read_options.set_crypto_pass(tmp_pass); read_options.set_crypto_size(param.crypto_size); read_options.set_input_pipe(param.input_pipe); read_options.set_output_pipe(param.output_pipe); read_options.set_execute(param.execute); read_options.set_info_details(param.info_details); read_options.set_lax(param.lax); read_options.set_sequential_read(param.sequential_read); read_options.set_slice_min_digits(param.num_digits); read_options.set_ignore_signature_check_failure(param.blind_signatures); read_options.set_multi_threaded_crypto(param.multi_threaded_crypto); read_options.set_multi_threaded_compress(param.multi_threaded_compress); if(repo) read_options.set_entrepot(repo); if(param.ref_filename != nullptr && param.ref_root != nullptr) { secu_string ref_tmp_pass; crypto_algo ref_crypto; line_tools_crypto_split_algo_pass(param.pass_ref, ref_crypto, ref_tmp_pass, no_cipher_given, recipients); read_options.set_external_catalogue(*param.ref_root, *param.ref_filename); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_ref_crypto_algo(crypto_algo::none); else read_options.set_ref_crypto_algo(ref_crypto); read_options.set_ref_crypto_pass(ref_tmp_pass); read_options.set_ref_crypto_size(param.crypto_size_ref); read_options.set_ref_execute(param.execute_ref); read_options.set_ref_slice_min_digits(param.ref_num_digits); if(ref_repo) read_options.set_ref_entrepot(ref_repo); } arch.reset(new (nothrow) archive(dialog, *param.sauv_root, param.filename, EXTENSION, read_options)); if(!arch) throw Ememory("little_main"); diff_options.clear(); diff_options.set_selection(*param.selection); diff_options.set_subtree(*param.subtree); diff_options.set_info_details(param.info_details); diff_options.set_display_treated(param.display_treated, param.display_treated_only_dir); diff_options.set_display_skipped(param.display_skipped); diff_options.set_ea_mask(*param.ea_mask); diff_options.set_what_to_check(param.what_to_check); diff_options.set_alter_atime(param.alter_atime); diff_options.set_furtive_read_mode(param.furtive_read_mode); diff_options.set_hourshift(param.hourshift); diff_options.set_compare_symlink_date(param.no_compare_symlink_date); diff_options.set_fsa_scope(param.scope); diff_options.set_in_place(param.in_place); st = arch->op_diff(*param.fs_root, diff_options, nullptr); if(!param.quiet) display_diff_stat(*dialog, st); if(st.get_errored() > 0 || st.get_deleted() > 0) throw Edata(gettext("Some file comparisons failed")); break; case test: line_tools_crypto_split_algo_pass(param.pass, crypto, tmp_pass, no_cipher_given, recipients); read_options.clear(); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_crypto_algo(crypto_algo::none); else read_options.set_crypto_algo(crypto); read_options.set_crypto_pass(tmp_pass); read_options.set_crypto_size(param.crypto_size); read_options.set_input_pipe(param.input_pipe); read_options.set_output_pipe(param.output_pipe); read_options.set_execute(param.execute); read_options.set_info_details(param.info_details); read_options.set_lax(param.lax); read_options.set_sequential_read(param.sequential_read); read_options.set_slice_min_digits(param.num_digits); read_options.set_ignore_signature_check_failure(param.blind_signatures); read_options.set_multi_threaded_crypto(param.multi_threaded_crypto); read_options.set_multi_threaded_compress(param.multi_threaded_compress); if(repo) read_options.set_entrepot(repo); if(param.ref_filename != nullptr && param.ref_root != nullptr) { secu_string ref_tmp_pass; crypto_algo ref_crypto; line_tools_crypto_split_algo_pass(param.pass_ref, ref_crypto, ref_tmp_pass, no_cipher_given, recipients); read_options.set_external_catalogue(*param.ref_root, *param.ref_filename); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_ref_crypto_algo(crypto_algo::none); else read_options.set_ref_crypto_algo(ref_crypto); read_options.set_ref_crypto_pass(ref_tmp_pass); read_options.set_ref_crypto_size(param.crypto_size_ref); read_options.set_ref_execute(param.execute_ref); read_options.set_ref_slice_min_digits(param.ref_num_digits); if(ref_repo) read_options.set_ref_entrepot(ref_repo); } arch.reset(new (nothrow) archive(dialog, *param.sauv_root, param.filename, EXTENSION, read_options)); if(!arch) throw Ememory("little_main"); test_options.clear(); test_options.set_selection(*param.selection); test_options.set_subtree(*param.subtree); test_options.set_info_details(param.info_details); test_options.set_display_treated(param.display_treated, param.display_treated_only_dir); test_options.set_display_skipped(param.display_skipped); test_options.set_empty(param.empty); st = arch->op_test(test_options, nullptr); if(!param.quiet) display_test_stat(*dialog, st); if(st.get_errored() > 0) throw Edata(gettext("Some files are corrupted in the archive and it will not be possible to restore them")); break; case listing: line_tools_crypto_split_algo_pass(param.pass, crypto, tmp_pass, no_cipher_given, recipients); read_options.clear(); if(no_cipher_given) // since archive format 9 crypto algo used // is stored in the archive, it will be used // unless we specify explicitely the cipher to use read_options.set_crypto_algo(crypto_algo::none); else read_options.set_crypto_algo(crypto); read_options.set_crypto_pass(tmp_pass); read_options.set_crypto_size(param.crypto_size); read_options.set_input_pipe(param.input_pipe); read_options.set_output_pipe(param.output_pipe); read_options.set_execute(param.execute); read_options.set_info_details(param.info_details); read_options.set_lax(param.lax); read_options.set_sequential_read(param.sequential_read); read_options.set_slice_min_digits(param.num_digits); read_options.set_ignore_signature_check_failure(param.blind_signatures); read_options.set_multi_threaded_crypto(param.multi_threaded_crypto); read_options.set_multi_threaded_compress(param.multi_threaded_compress); if(repo) read_options.set_entrepot(repo); read_options.set_header_only(param.header_only); arch.reset(new (nothrow) archive(dialog, *param.sauv_root, param.filename, EXTENSION, read_options)); if(!arch) throw Ememory("little_main"); if(param.quiet) { const list & gnupg_signed = arch->get_signatories(); arch->summary(); line_tools_display_signatories(*dialog, gnupg_signed); } else { if(param.info_details) { const list & gnupg_signed = arch->get_signatories(); arch->summary(); line_tools_display_signatories(*dialog, gnupg_signed); dialog->pause(gettext("Continue listing archive contents?")); } listing_options.clear(); listing_options.set_info_details(param.info_details); listing_options.set_list_mode(param.list_mode); listing_options.set_selection(*param.selection); listing_options.set_subtree(*param.subtree); listing_options.set_filter_unsaved(param.filter_unsaved); listing_options.set_display_ea(param.list_ea); if(!param.file_size.is_zero() && param.list_mode == archive_options_listing_shell::slicing) listing_options.set_user_slicing(param.first_file_size, param.file_size); listing_options.set_sizes_in_bytes(param.sizes_in_bytes); shelli->archive_show_contents(*arch, listing_options); } break; default: throw SRC_BUG; } } catch(...) { if(!param.quiet) dialog->message(gettext("Final memory cleanup...")); throw; } if(param.info_details) dialog->message(gettext("Final memory cleanup...")); return ret; } } static void display_sauv_stat(user_interaction & dialog, const statistics & st) { infinint total = st.total(); infinint treated = st.get_treated(); infinint hard_links = st.get_hard_links(); infinint tooold = st.get_tooold(); infinint skipped = st.get_skipped(); infinint inode_only = st.get_inode_only(); infinint ignored = st.get_ignored(); infinint errored = st.get_errored(); infinint deleted = st.get_deleted(); infinint ea_treated = st.get_ea_treated(); infinint fsa_treated = st.get_fsa_treated(); infinint byte_count = st.get_byte_amount(); dialog.printf("\n\n --------------------------------------------\n"); dialog.printf(gettext(" %i inode(s) saved\n"), &treated); dialog.printf(gettext(" including %i hard link(s) treated\n"), &hard_links); dialog.printf(gettext(" %i inode(s) changed at the moment of the backup and could not be saved properly\n"), &tooold); dialog.printf(gettext(" %i byte(s) have been wasted in the archive to resave changing files"), & byte_count); dialog.printf(gettext(" %i inode(s) with only metadata changed\n"), &inode_only); dialog.printf(gettext(" %i inode(s) not saved (no inode/file change)\n"), &skipped); dialog.printf(gettext(" %i inode(s) failed to be saved (filesystem error)\n"), &errored); dialog.printf(gettext(" %i inode(s) ignored (excluded by filters)\n"), &ignored); dialog.printf(gettext(" %i inode(s) recorded as deleted from reference backup\n"), &deleted); dialog.printf(" --------------------------------------------\n"); dialog.printf(gettext(" Total number of inode(s) considered: %i\n"), &total); #ifdef EA_SUPPORT dialog.printf(" --------------------------------------------\n"); dialog.printf(gettext(" EA saved for %i inode(s)\n"), &ea_treated); #endif dialog.printf(gettext(" FSA saved for %i inode(s)\n"), &fsa_treated); dialog.printf(" --------------------------------------------\n"); } static void display_rest_stat(user_interaction & dialog, const statistics & st) { infinint total = st.total(); infinint treated = st.get_treated(); infinint hard_links = st.get_hard_links(); infinint tooold = st.get_tooold(); infinint skipped = st.get_skipped(); infinint ignored = st.get_ignored(); infinint errored = st.get_errored(); infinint deleted = st.get_deleted(); infinint ea_treated = st.get_ea_treated(); infinint fsa_treated = st.get_fsa_treated(); dialog.printf("\n\n --------------------------------------------\n"); dialog.printf(gettext(" %i inode(s) restored\n"), &treated); dialog.printf(gettext(" including %i hard link(s)\n"), &hard_links); dialog.printf(gettext(" %i inode(s) not restored (not saved in archive)\n"), &skipped); dialog.printf(gettext(" %i inode(s) not restored (overwriting policy decision)\n"), &tooold); dialog.printf(gettext(" %i inode(s) ignored (excluded by filters)\n"), &ignored); dialog.printf(gettext(" %i inode(s) failed to restore (filesystem error)\n"), &errored); dialog.printf(gettext(" %i inode(s) deleted\n"), &deleted); dialog.printf(" --------------------------------------------\n"); dialog.printf(gettext(" Total number of inode(s) considered: %i\n"), &total); #ifdef EA_SUPPORT dialog.printf(" --------------------------------------------\n"); dialog.printf(gettext(" EA restored for %i inode(s)\n"), &ea_treated); #endif dialog.printf(gettext(" FSA restored for %i inode(s)\n"), &fsa_treated); dialog.printf(" --------------------------------------------\n"); } static void display_diff_stat(user_interaction & dialog, const statistics &st) { infinint total = st.total(); infinint treated = st.get_treated(); infinint ignored = st.get_ignored(); infinint errored = st.get_errored(); dialog.printf("\n\n --------------------------------------------\n"); dialog.printf(gettext(" %i item(s) treated\n"), &treated); dialog.printf(gettext(" %i item(s) do not match those on filesystem\n"), &errored); dialog.printf(gettext(" %i item(s) ignored (excluded by filters)\n"), &ignored); dialog.printf(" --------------------------------------------\n"); dialog.printf(gettext(" Total number of items considered: %i\n"), &total); dialog.printf(" --------------------------------------------\n"); } static void display_test_stat(user_interaction & dialog, const statistics & st) { infinint total = st.total(); infinint treated = st.get_treated(); infinint errored = st.get_errored(); infinint skipped = st.get_skipped(); dialog.printf("\n\n --------------------------------------------\n"); dialog.printf(gettext(" %i item(s) treated\n"), &treated); dialog.printf(gettext(" %i item(s) with error\n"), &errored); dialog.printf(gettext(" %i item(s) ignored (excluded by filters)\n"), &skipped); dialog.printf(" --------------------------------------------\n"); dialog.printf(gettext(" Total number of items considered: %i\n"), &total); dialog.printf(" --------------------------------------------\n"); } static void display_merge_stat(user_interaction & dialog, const statistics & st) { infinint total = st.total(); infinint treated = st.get_treated(); infinint hard_links = st.get_hard_links(); infinint deleted = st.get_deleted(); infinint ignored = st.get_ignored(); infinint ea_treated = st.get_ea_treated(); infinint fsa_treated = st.get_fsa_treated(); dialog.printf("\n\n --------------------------------------------\n"); dialog.printf(gettext(" %i inode(s) added to archive\n"), &treated); dialog.printf(gettext(" with %i hard link(s) recorded\n"), &hard_links); dialog.printf(gettext(" %i inode(s) ignored (excluded by filters)\n"), &ignored); dialog.printf(gettext(" %i inode(s) recorded as deleted\n"), &deleted); #ifdef EA_SUPPORT dialog.printf(" --------------------------------------------\n"); dialog.printf(gettext(" EA saved for %i inode(s)\n"), &ea_treated); #endif dialog.printf(gettext(" FSA saved for %i inode(s)\n"), &fsa_treated); dialog.printf(" --------------------------------------------\n"); dialog.printf(gettext(" Total number of inode(s) considered: %i\n"), &total); dialog.printf(" --------------------------------------------\n"); } const char *dar_version() { return DAR_VERSION; } dar-2.7.15/src/dar_suite/line_tools.hpp0000644000175000017500000004424114636067146014745 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file line_tools.hpp /// \brief a set of general command line targeted routines /// \ingroup CMDLINE #ifndef LINE_TOOLS_HPP #define LINE_TOOLS_HPP #include "../my_config.h" #include #include #include #include #include "libdar.hpp" #include "tlv_list.hpp" #include "tools.hpp" using namespace libdar; /// \addtogroup CMDLINE /// @{ constexpr U_I LINE_TOOLS_SI_SUFFIX = 1000; constexpr U_I LINE_TOOLS_BIN_SUFFIX = 1024; class argc_argv { public: argc_argv(S_I size = 0); argc_argv(const argc_argv & ref) { throw Efeature("argc_argv"); }; argc_argv & operator = (const argc_argv & ref) { throw Efeature("argc_argv"); }; ~argc_argv() noexcept(false); void resize(S_I size); void set_arg(const std::string & arg, S_I index); void set_arg(generic_file & f, U_I size, S_I index); S_I argc() const { return x_argc; }; char* const * argv() const { return x_argv; }; // well, the const method is a bit silly, as the caller has the possibility to modify what is pointed to by the returned value... private: S_I x_argc; char **x_argv; }; extern void line_tools_slice_ownership(const std::string & cmd, std::string & slice_permission, std::string & slice_user_ownership, std::string & slice_group_ownership); extern void line_tools_repeat_param(const std::string & cmd, infinint & repeat_count, infinint & repeat_byte); extern void line_tools_tlv_list2argv(user_interaction & dialog, tlv_list & list, argc_argv & arg); /// returns the old position of parsing (next argument to parse) extern S_I line_tools_reset_getopt(); std::string::const_iterator line_tools_find_first_char_out_of_parenth(const std::string & argument, unsigned char to_find); std::string::const_iterator line_tools_find_last_char_out_of_parenth(const std::string & argument, unsigned char to_find); std::string line_tools_expand_user_comment(const std::string & user_comment, S_I argc, char *const argv[]); /// split a PATH environement variable string into its components (/usr/lib:/lib => /usr/lib /lib) std::deque line_tools_explode_PATH(const char *the_path); /// return the full path of the given filename (eventually unchanged of pointing to the first file of that name present in the_path directories std::string line_tools_get_full_path_from_PATH(const std::deque & the_path, const char * filename); /// return split at the first space met the string given as first argument, and provide the two splitted string as second and third argument void line_tools_split_at_first_space(const char *field, std::string & before_space, std::string & after_space); void line_tools_get_min_digits(std::string arg, infinint & num, infinint & ref_num, infinint & aux_num); /// test the presence of a set of argument on the command line /// \param[in] arguments is the list of options to look for /// \param[in] argc is the number of argument on the command line /// \param[in] argv is the list of arguments on the command line /// \param[in] getopt_string is the parsing string to pass to getopt #if HAVE_GETOPT_LONG /// \param[in] long_options is the optional list of long options (an nullptr pointer is acceptable for no long option) #endif /// \param[in] stop_scan if this (char) option is met, stop scanning for wanted options /// \param[out] presence is a subset of arguments containing the option found on command-line extern void line_tools_look_for(const std::deque & arguments, S_I argc, char *const argv[], const char *getopt_string, #if HAVE_GETOPT_LONG const struct option *long_options, #endif char stop_scan, std::deque & presence); /// test the presence of -Q and -j options on the command line /// \param[in] argc is the number of argument on the command line /// \param[in] argv is the list of arguments on the command line /// \param[in] getopt_string is the parsing string to pass to getopt #if HAVE_GETOPT_LONG /// \param[in] long_options is the optional list of long options (an nullptr pointer is acceptable for no long option) #endif /// \param[in] stop_scan if this (char) option is met, stop scanning for -j and -Q options /// \param[out] Q_is_present is set to true if -Q option or its equivalent long option has been found on command-line extern void line_tools_look_for_Q(S_I argc, char *const argv[], const char *getopt_string, #if HAVE_GETOPT_LONG const struct option *long_options, #endif char stop_scan, bool & Q_is_present); /// split a line in words given the separator character (sep) template void line_tools_split(const std::string & val, char sep, T & split) { std::string::const_iterator be = val.begin(); std::string::const_iterator ne = val.begin(); split.clear(); while(ne != val.end()) { if(*ne != sep) ++ne; else { split.push_back(std::string(be, ne)); ++ne; be = ne; } } if(be != val.end()) split.push_back(std::string(be, ne)); } extern std::set line_tools_deque_to_set(const std::deque & list); extern void line_tools_4_4_build_compatible_overwriting_policy(bool allow_over, bool detruire, bool more_recent, const libdar::infinint & hourshift, bool ea_erase, const libdar::crit_action * & overwrite); /// split the argument to -K, -J and -$ in their different parts /// \param[in] all is what the user provided on command-line /// \param[out] algo is the symmetrical algorithm to use /// \param[out] pass is either the passphrase /// \param[out] no_cipher_given is true if the use did not specified the cipher (which defaults to blowfish) /// \param[out] recipients emails recipients to use (empty list if gnupg has not to be used) extern void line_tools_crypto_split_algo_pass(const secu_string & all, crypto_algo & algo, secu_string & pass, bool & no_cipher_given, std::vector & recipients); /// display information about the signatories extern void line_tools_display_signatories(user_interaction & ui, const std::list & gnupg_signed); /// Extract from anonymous pipe a tlv_list /// \param[in,out] dialog for user interaction /// \param[in] fd the filedescriptor for the anonymous pipe's read extremity /// \param[out] result the resulting tlv_list extern void line_tools_read_from_pipe(std::shared_ptr & dialog, S_I fd, tlv_list & result); /// extracts the basename of a file (removing path part) /// \param[in] command_name is the full path of the file /// \param[out] basename the basename of the file /// \exception Ememory can be thrown if memory allocation failed extern void line_tools_extract_basename(const char *command_name, std::string & basename); /// give a pointer to the last character of the given value in the given string /// \param[in] s is the given string /// \param[in] v is the given char value /// \return a interator on s, pointing on the first char of s equal to v or a pointing to s.end() if no such char could be found is "s" /// \note the arguments are not modified neither the data they are pointing to. However the const statement has not been used to /// be able to return a iterator on the string (and not a const_interator). There is probably other ways to do that (using const_cast) for example extern std::string::iterator line_tools_find_first_char_of(std::string &s, unsigned char v); /// split a given full path in path part and basename part /// \param[in] all is the path to split /// \param[out] chemin is the resulting path part, it points to a newly allocated path object /// \param[out] base is the resulting basename /// \note chemin argument must be release by the caller thanks to the "delete" operator. extern void line_tools_split_path_basename(const char *all, path * &chemin, std::string & base); /// split a given full path in path part and basename part /// \param[in] all is the path to split /// \param[out] chemin is the resulting path part, it points to a newly allocated path object /// \param[out] base is the resulting basename /// \note chemin argument must be release by the caller thanks to the "delete" operator. extern void line_tools_split_path_basename(const std::string &all, std::string & chemin, std::string & base); /// split a given full remote repository path in parts /// \param[in] all is the argument to split in parts /// \param[out] proto is the protocol field /// \param[out] login is the login field (empty string is returned if not provided) /// \param[out] password is the password field (empty string if not provided) /// \param[out] hostname is the hostname field /// \param[out] port is the port field (empty string if not provided) /// \param[out] path_basename is the path+basename remaing field /// \return false if the all argument does not follow the remote repository syntax extern bool line_tools_split_entrepot_path(const std::string &all, std::string & proto, std::string & login, secu_string & password, std::string & hostname, std::string & port, std::string & path_basename); /// convert a signed integer written in decimal notation to the corresponding value /// \param[in] x the decimal representation of the integer /// \return the value corresponding to the decimal representation given extern S_I line_tools_str2signed_int(const std::string & x); /// convert a human readable date representation in number of second since the system reference date /// \param[in] repres the date's human representation /// \return the corresponding number of seconds (computer time) /// \note the string expected format is "[[[year/]month/]day-]hour:minute[:second]" extern infinint line_tools_convert_date(const std::string & repres); /// display the compilation time features of libdar /// \param[in,out] dialog for user interaction /// \note this call uses the compile_time:: routines, and will /// not change its interface upon new feature addition extern void line_tools_display_features(user_interaction & dialog); /// isolate the value of a given variable from the environment vector /// \param[in] env the environment vector as retreived from the third argument of the main() function /// \param[in] clef the key or variable name too look for /// \return nullptr if the key could not be found or a pointer to the env data giving the value of the requested key /// \note the returned value must not be released by any mean as it is just a pointer to an system allocated memory (the env vector). extern const char *line_tools_get_from_env(const char **env, const char *clef); /// does sanity checks on a slice name, check presence and detect whether the given basename is not rather a filename /// \param[in,out] dialog for user interaction /// \param[in] loc the path where resides the slice /// \param[in,out] base the basename of the slice /// \param[in] extension the extension of dar's slices /// \param[in] create whether this is a new archive that is about to be created by this name /// \note if user accepted the change of slice name proposed by libdar through dialog the base argument is changed extern void line_tools_check_basename(user_interaction & dialog, const path & loc, std::string & base, const std::string & extension, bool create); /// if a slice number 1 is met with the provided basename, set the num_digits accordingly /// \param[in,out] dialog for user interaction /// \param[in] loc the path where are expected the slices to be present /// \param[in] base the basename of the archive /// \param[in] extension the extension of dar's slices /// \param[in,out] num_digits the min width of slice number (0 padded numbers) void line_tools_check_min_digits(user_interaction & dialog, const path & loc, const std::string & base, const std::string & extension, infinint & num_digits); /// from a string with a range notation (min-max) extract the range values /// \param[in] s the string to parse /// \param[out] min the minimum value of the range /// \param[out] max the maximum value of the range /// \exception Erange is thrown is the string to parse is incorrect /// \note: either a single number (positive or negative) is returned in min /// (max is set to min if min is positive or to zero if min is negative) /// or a range of positive numbers. extern void line_tools_read_range(const std::string & s, S_I & min, U_I & max); /// read a file and split its contents into words /// \param[in,out] f is the file to read /// \param[out] mots std container to receive the split result /// \return the list of words found in this order in the file /// \note The different quotes are taken into account template void line_tools_split_in_words(generic_file & f, T & mots) { std::deque quotes; std::string current = ""; char a; bool loop = true; bool escaped = false; mots.clear(); while(loop) { if(f.read(&a, 1) != 1) // reached end of file { loop = false; a = ' '; // to close the last word } if(escaped) { current += a; // added without consideration of quoting of any sort escaped = false; continue; // continuing at beginning of the while loop } else { if(a == '\\') { escaped = true; continue; // continuing at beginning of the while loop } } if(quotes.empty()) // outside a word switch(a) { case ' ': case '\t': case '\n': case '\r': break; case '"': case '\'': case '`': quotes.push_back(a); break; default: quotes.push_back(' '); // the quote space means no quote current += a; // a new argument is starting break; } else // inside a word switch(a) { case '\t': if(quotes.back() != ' ') { // this is the end of the wor(l)d ;-) // ...once again... 1000, 1999, 2012, and the next ones to come... break; } // no break ! case '\n': case '\r': a = ' '; // replace carriage return inside quoted string by a space // no break ! case ' ': case '"': case '\'': case '`': if(a == quotes.back()) // "a" is an ending quote { quotes.pop_back(); if(quotes.empty()) // reached end of word { mots.push_back(current); current = ""; } else current += a; } else // "a" is a nested starting quote { if(a != ' ') // quote ' ' does not have ending quote quotes.push_back(a); current += a; } break; default: current += a; } } if(!quotes.empty()) throw Erange("make_args_from_file", tools_printf(dar_gettext("Parse error: Unmatched `%c'"), quotes.back())); } /// read a std::string and split its contents into words /// \param[in,out] arg is the string to read /// \param[out] mots a std container to receive the split result /// \return the list of words found in this order in the file /// \note The different quotes are taken into account template void line_tools_split_in_words(const std::string & arg, T & mots) { memory_file mem; mem.write(arg.c_str(), arg.size()); mem.skip(0); line_tools_split_in_words(mem, mots); } /// builds a regex from root directory and user provided regex to be applied to the relative path /// \param[in] prefix is the root portion of the path /// \param[in] relative_part is the user provided regex to be applied to the relative path /// \return the corresponding regex to be applied to full absolute path extern std::string line_tools_build_regex_for_exclude_mask(const std::string & prefix, const std::string & relative_part); /// return a string containing the Effective UID extern std::string line_tools_get_euid(); /// return a string containing the Effective UID extern std::string line_tools_get_egid(); /// return a string containing the hostname of the current host extern std::string line_tools_get_hostname(); /// return a string containing the current time (UTC) extern std::string line_tools_get_date_utc(); /// add in 'a', element of 'b' not already found in 'a' extern void line_tools_merge_to_deque(std::deque & a, const std::deque & b); /// remove from 'a' elements found in 'b' and return the resulting deque extern std::deque line_tools_substract_from_deque(const std::deque & a, const std::deque & b); /// converts string name to function /// \note throws Erange in case of error extern delta_sig_block_size::fs_function_t line_tools_string_to_sig_block_size_function(const std::string & funname); extern void line_tools_split_compression_algo(const char *arg, ///< input string to analyse U_I base, ///< base value for number suffix compression & algo, ///< returned compression algorithm U_I & level, ///< returned compression level U_I & block_size ///< returned compression block size ); /// @} #endif dar-2.7.15/src/dar_suite/line_tools.cpp0000644000175000017500000013365714636067146014752 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include "../my_config.h" extern "C" { #if HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_STRING_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_FCNTL_H #include #endif #include "getopt_decision.h" } #include #include #include #include #include "line_tools.hpp" #include "tools.hpp" #include "tuyau.hpp" #include "fichier_local.hpp" #include "etage.hpp" #include "nls_swap.hpp" using namespace std; using namespace libdar; //// #define YES_NO(x) (x ? gettext("YES") : gettext("NO")) static string build(string::iterator a, string::iterator b); static bool is_a_slice_available(user_interaction & ui, const string & base, const string & extension); static string retreive_basename(const string & base, const string & extension); static string retreive_basename2(const string & base); static void tools_localtime(const time_t & timep, struct tm *result); //// argc_argv::argc_argv(S_I size) { x_argc = size; if(x_argc > 0) { x_argv = new (nothrow) char *[size]; if(x_argv == nullptr) throw Ememory("argc_argv::argc_argv"); for(S_I i = 0; i < x_argc; i++) x_argv[i] = nullptr; } else x_argv = nullptr; } argc_argv::~argc_argv() noexcept(false) { if(x_argv == nullptr && x_argc > 0) throw SRC_BUG; for(S_I i = 0; i < x_argc; i++) if(x_argv[i] != nullptr) { delete [] x_argv[i]; x_argv[i] = nullptr; } if(x_argc > 0) delete [] x_argv; } void argc_argv::set_arg(const string & arg, S_I index) { if(index >= x_argc) throw Erange("argc_argv::set_arg", gettext("Index out of range")); if(x_argv[index] != nullptr) { delete [] x_argv[index]; x_argv[index] = nullptr; } x_argv[index] = new (nothrow) char[arg.size() + 1]; if(x_argv[index] == nullptr) throw Ememory("argc_argv::set_arg"); strncpy(x_argv[index], arg.c_str(), arg.size()); x_argv[index][arg.size()] = '\0'; } void argc_argv::set_arg(generic_file & f, U_I size, S_I index) { if(index >= x_argc) throw Erange("argc_argv::set_arg", gettext("Index out of range")); if(x_argv[index] != nullptr) { delete [] x_argv[index]; x_argv[index] = nullptr; } x_argv[index] = new (nothrow) char [size+1]; if(x_argv[index] == nullptr) throw Ememory("argc_argv::set_arg"); x_argv[index][f.read(x_argv[index], size)] = '\0'; } void argc_argv::resize(S_I size) { char **tmp = nullptr; if(size == x_argc) return; if(size < x_argc) for(S_I i = size; i < x_argc; i++) if(x_argv[i] != nullptr) { delete [] x_argv[i]; x_argv[i] = nullptr; } tmp = new (nothrow) char*[size]; if(tmp == nullptr) throw Ememory("argc_argv::resize"); try { S_I min = size < x_argc ? size : x_argc; for(S_I i = 0; i < min; i++) tmp[i] = x_argv[i]; for(S_I i = min; i < size; i++) tmp[i] = nullptr; if(x_argc > 0) delete [] x_argv; x_argv = tmp; tmp = nullptr; x_argc = size; } catch(...) { if(tmp != nullptr) delete [] tmp; throw; } } void line_tools_slice_ownership(const string & cmd, string & slice_permission, string & slice_user_ownership, string & slice_group_ownership) { string::iterator s1, s2; s1 = const_cast(&cmd)->begin(); // looking for the first ':' while(s1 != cmd.end() && *s1 != ':') s1++; s2 = s1; if(s2 != cmd.end()) s2++; // looking for the second ':' while(s2 != cmd.end() && *s2 != ':') s2++; if(s1 == cmd.begin()) slice_permission = ""; else slice_permission = build(const_cast(&cmd)->begin(), s1); if(s1 == cmd.end()) slice_user_ownership = ""; else slice_user_ownership = build(s1+1, s2); if(s2 == cmd.end()) slice_group_ownership = ""; else slice_group_ownership = build(s2+1, const_cast(&cmd)->end()); } void line_tools_repeat_param(const string & cmd, infinint & repeat_count, infinint & repeat_byte) { string::iterator s1; string tmp1, tmp2; s1 = const_cast(&cmd)->begin(); // looking for the first ':' while(s1 != cmd.end() && *s1 != ':') s1++; if(s1 != cmd.end()) // thus *s1 == ':' { tmp1 = build(const_cast(&cmd)->begin(), s1); tmp2 = build(s1+1, const_cast(&cmd)->end()); } else { tmp1 = cmd; tmp2 = "0"; } try { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci x1 = tmp1; libdar::deci x2 = tmp2; repeat_count = x1.computer(); repeat_byte = x2.computer(); } catch(Edeci & e) { throw Erange("line_tools_repeat_param", string(gettext("Syntax error in --retry-on-change argument: ")) + e.get_message()); } } void line_tools_tlv_list2argv(user_interaction & dialog, tlv_list & list, argc_argv & arg) { memory_file mem = memory_file(); U_I transfert = 0; infinint size; arg.resize(list.size()); for(S_I i = 0; i < arg.argc() ; i++) { if(list[i].get_type() != 0) // we only use type 0 here throw Erange("line_tools_tlv_list2argv", gettext("Unknown TLV record type")); size = list[i].size(); transfert = 0; size.unstack(transfert); if(!size.is_zero()) throw Erange("line_tools_tlv_list2argv", "Too long argument found in TLV to be handled by standard library routine"); list[i].skip(0); arg.set_arg(list[i], transfert, i); } } S_I line_tools_reset_getopt() { S_I ret = optind; #if HAVE_OPTRESET optreset = 1; optind = 1; #else optind = 0; #endif return ret; } string::const_iterator line_tools_find_first_char_out_of_parenth(const string & argument, unsigned char to_find) { string::const_iterator it = argument.begin(); U_I parenth = 0; while(it != argument.end() && (*it != to_find || parenth > 0)) { switch(*it) { case '(': ++parenth; break; case ')': if(parenth > 0) --parenth; else throw Erange("line_tools_find_first_char_out_of_parenth", string(gettext("Unbalanced parenthesis in expression: ")) + argument); break; // no default: statement needed } ++it; } return it; } string::const_iterator line_tools_find_last_char_out_of_parenth(const string & argument, unsigned char to_find) { string::const_iterator it = argument.begin(); string::const_iterator back = it; U_I parenth = 0; while(it != argument.end()) { if(*it == to_find && parenth == 0) back = it; switch(*it) { case '(': ++parenth; break; case ')': if(parenth > 0) --parenth; else throw Erange("line_tools_find_first_char_out_of_parenth", string(gettext("Unbalanced parenthesis in expression: ")) + argument); break; // no default: statement needed } ++it; } if(back == argument.begin()) if(back != argument.end() && *back != to_find) back = argument.end(); return back; } string line_tools_expand_user_comment(const string & user_comment, S_I argc, char *const argv[]) { string::const_iterator it = user_comment.begin(); string::const_iterator st = it; string ret = ""; while(it != user_comment.end()) { if(*it == '%') { it++; if(it != user_comment.end()) { ret += string(st, it - 1); st = it+1; switch(*it) { case 'c': ret += string("\"") + argv[0] + "\""; for(S_I i = 1; i < argc; ++i) if(strcmp(argv[i], "-K") == 0 || strcmp(argv[i], "-J") == 0 || strcmp(argv[i], "-$") == 0 || strcmp(argv[i], "-#") == 0 || strcmp(argv[i], "-*") == 0 || strcmp(argv[i], "-%") == 0 || strcmp(argv[i], "--key") == 0 || strcmp(argv[i], "--ref-key") == 0 || strcmp(argv[i], "--aux-key") == 0 || strcmp(argv[i], "--crypto-block") == 0 || strcmp(argv[i], "--ref-crypto-block") == 0 || strcmp(argv[i], "--aux-crypto-block") == 0) ++i; else ret += string(" \"") + argv[i] + "\""; break; case 'd': ret += line_tools_get_date_utc(); break; case 'u': ret += line_tools_get_euid(); break; case 'g': ret += line_tools_get_egid(); break; case 'h': ret += line_tools_get_hostname(); break; case '%': ret += "%"; break; default: throw Erange("line_tools_expand_user_comment", tools_printf(gettext("Unknown macro %%%d in user comment"), *it)); } } } it++; } if(st != user_comment.end()) ret += string(st, user_comment.end()); return ret; } deque line_tools_explode_PATH(const char *the_path) { deque ret; const char *it = the_path; const char *last = it; ret.clear(); if(it == nullptr) return ret; while(*it != '\0') { if(*it == ':') { ret.push_back(string(last, it)); ++it; last = it; } else ++it; } ret.push_back(string(last, it)); return ret; } string line_tools_get_full_path_from_PATH(const deque & the_path, const char * filename) { string ret; bool no_path = false; if(filename == nullptr) throw SRC_BUG; else { try { path tmp(filename); if(!tmp.is_relative()) no_path = false; // no need to check if file exist using the_path, path is absolute else if(tmp.degre() != 1) no_path = false; // this is a composed path, we must not inspect the_path else no_path = true; // this is just the filename without path indication, so we check the_path } catch(Erange & e) { if(e.get_source() == "path::path") no_path = false; // not a valid path else throw; } } if(!no_path) ret = filename; else { deque::const_iterator it = the_path.begin(); bool found = false; while(!found && it != the_path.end()) { try { string where = (path(*it).append(filename)).display(); fichier_local tmp = fichier_local(where, false); found = true; ret = where; } catch(...) { ++it; } } if(!found) ret = filename; } return ret; } void line_tools_split_at_first_space(const char *field, string & before_space, string & after_space) { const char *ptr = field; if(field == nullptr) throw SRC_BUG; while(*ptr != ' ' && *ptr != '\0') ++ptr; if(*ptr == '\0') { before_space = field; after_space = ""; } else { before_space = string(field, ptr); after_space = string(ptr + 1); } } void line_tools_get_min_digits(string the_arg, infinint & num, infinint & ref_num, infinint & aux_num) { num = ref_num = aux_num = 0; string::iterator it1, it2; if(the_arg == "") return; try { it1 = line_tools_find_first_char_of(the_arg, ','); if(it1 == the_arg.end()) // a single number is provided { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci tmp = the_arg; num = tmp.computer(); } else // at least two numbers are provided { if(the_arg.begin() != it1) { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci convert = string(the_arg.begin(), it1); num = convert.computer(); } // else we ignore the leading ',' ++it1; if(it1 == the_arg.end()) return; // trailing ',' has to be ignored string tmp2 = string(it1, the_arg.end()); it2 = line_tools_find_first_char_of(tmp2, ','); if(it2 == tmp2.end()) // just two number have been provided { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci convert = tmp2; ref_num = convert.computer(); } else { if(tmp2.begin() != it2) { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci convert = string(tmp2.begin(), it2); ref_num = convert.computer(); } ++it2; if(it2 != tmp2.end()) { // note that the namespace specification is necessary // due to similar existing name in std namespace under // certain OS (FreeBSD 10.0) libdar::deci convert = string(it2, tmp2.end()); aux_num = convert.computer(); } } } } catch(Edeci & e) { throw Erange("line_tools_get_min_digits", tools_printf(gettext("Invalid number in string: %S"), &the_arg)); } } void line_tools_look_for(const deque & arguments, S_I argc, char *const argv[], const char *getopt_string, #if HAVE_GETOPT_LONG const struct option *long_options, #endif char stop_scan, deque & presence) { S_I lu; presence.clear(); (void)line_tools_reset_getopt(); #if HAVE_GETOPT_LONG const struct option *ptr_long_opt = long_options; const struct option voided = { nullptr, 0, nullptr, 0 }; if(long_options == nullptr) ptr_long_opt = &voided; while((lu = getopt_long(argc, argv, getopt_string, ptr_long_opt, nullptr)) != EOF && stop_scan != lu) #else while((lu = getopt(argc, argv, getopt_string)) != EOF && stop_scan != lu) #endif { deque::const_iterator it = find(arguments.begin(), arguments.end(), (char)lu); if(it != arguments.end()) presence.push_back(lu); } (void)line_tools_reset_getopt(); } void line_tools_look_for_Q(S_I argc, char *const argv[], const char *getopt_string, #if HAVE_GETOPT_LONG const struct option *long_options, #endif char stop_scan, bool & Q_is_present) { deque arguments; deque presence; deque::const_iterator it; Q_is_present = false; arguments.push_back('j'); arguments.push_back('Q'); line_tools_look_for(arguments, argc, argv, getopt_string, #if HAVE_GETOPT_LONG long_options, #endif stop_scan, presence); it = find(presence.begin(), presence.end(), 'Q'); if(it != presence.end()) Q_is_present = true; } set line_tools_deque_to_set(const deque & list) { set ret; deque::const_iterator it = list.begin(); while(it != list.end()) { ret.insert(*it); ++it; } return ret; } void line_tools_4_4_build_compatible_overwriting_policy(bool allow_over, bool detruire, bool more_recent, const infinint & hourshift, bool ea_erase, const crit_action * & overwrite) { crit_action *tmp1 = nullptr; crit_action *tmp2 = nullptr; // tmp1 and tmp2 are used for construction of the overwriting policy overwrite = nullptr; try { if(allow_over) { if(ea_erase) overwrite = new (nothrow) crit_constant_action(data_overwrite, EA_overwrite); else overwrite = new (nothrow) crit_constant_action(data_overwrite, EA_merge_overwrite); if(overwrite == nullptr) throw Ememory("tools_build_compatible_overwriting_policy"); tmp1 = new (nothrow) crit_constant_action(data_preserve, EA_preserve); if(tmp1 == nullptr) throw Ememory("tools_build_compatible_overwriting_policy"); if(more_recent) { tmp2 = new (nothrow) testing(crit_in_place_data_more_recent(hourshift), *tmp1, *overwrite); if(tmp2 == nullptr) throw Ememory("tools_build_compatible_overwriting_policy"); delete overwrite; overwrite = tmp2; tmp2 = nullptr; } if(!detruire) { tmp2 = new (nothrow) testing(crit_invert(crit_in_place_is_inode()), *overwrite, *tmp1); if(tmp2 == nullptr) throw Ememory("tools_build_compatible_overwriting_policy"); delete overwrite; overwrite = tmp2; tmp2 = nullptr; } delete tmp1; tmp1 = nullptr; } else { overwrite = new (nothrow) crit_constant_action(data_preserve, EA_preserve); if(overwrite == nullptr) throw Ememory("tools_build_compatible_overwriting_policy"); } if(overwrite == nullptr) throw SRC_BUG; if(tmp1 != nullptr) throw SRC_BUG; if(tmp2 != nullptr) throw SRC_BUG; } catch(...) { if(tmp1 != nullptr) delete tmp1; if(tmp2 != nullptr) delete tmp2; if(overwrite != nullptr) { delete overwrite; overwrite = nullptr; } throw; } } void line_tools_crypto_split_algo_pass(const secu_string & all, crypto_algo & algo, secu_string & pass, bool & no_cipher_given, vector & recipients) { // split from "algo:pass" "algo" "pass" "algo:" ":pass" syntaxes const char *it = all.c_str(); const char *fin = all.c_str() + all.get_size(); // points past the last byte of the secu_string "all" secu_string tmp; recipients.clear(); if(all.get_size() == 0) { algo = crypto_algo::none; pass.clear(); } else { while(it != fin && *it != ':') ++it; if(it != fin) // a ':' is present in the given string { // tmp holds the string before ':' tmp = secu_string(all.c_str(), it - all.c_str()); ++it; // pass holds the string after ':' pass = secu_string(it, fin - it); no_cipher_given = (tmp == ""); if(tmp == "scrambling" || tmp == "scram") algo = crypto_algo::scrambling; else if(tmp == "none") algo = crypto_algo::none; else if(tmp == "blowfish" || tmp == "bf") algo = crypto_algo::blowfish; else if(tmp == "aes" || tmp == "aes256" || tmp == "") algo = crypto_algo::aes256; // AES is the default cypher ("") else if(tmp == "twofish" || tmp == "twofish256") algo = crypto_algo::twofish256; else if(tmp == "serpent" || tmp == "serpent256") algo = crypto_algo::serpent256; else if(tmp == "camellia" || tmp == "camellia256") algo = crypto_algo::camellia256; else if(tmp == "gnupg") { secu_string emails; line_tools_crypto_split_algo_pass(pass, algo, emails, no_cipher_given, recipients); line_tools_split(emails.c_str(), ',', recipients); pass.clear(); } else throw Erange("crypto_split_algo_pass", string(gettext("unknown cryptographic algorithm: ")) + tmp.c_str()); } else // no ':' using blowfish as default cypher { no_cipher_given = true; algo = crypto_algo::aes256; pass = all; } } } void line_tools_display_signatories(user_interaction & ui, const list & gnupg_signed) { list::const_iterator it = gnupg_signed.begin(); string tmp; if(gnupg_signed.empty()) return; ui.printf( "+-----------------+----------------+----------------------------------------+-------------------------+"); ui.printf(gettext("| Signature Status| Key Status | Finger Print | Signature Date |")); ui.printf( "+-----------------+----------------+----------------------------------------+-------------------------+"); while(it != gnupg_signed.end()) { tmp = ""; switch(it->result) { case signator::good: tmp += "| Good |"; break; case signator::bad: tmp += "| BAD !!! |"; break; case signator::unknown_key: tmp += "| Unknown Key |"; break; case signator::error: tmp += "| System Error |"; break; default: throw SRC_BUG; } switch(it->key_validity) { case signator::valid: tmp += " Valid |"; break; case signator::expired: tmp += " EXPIRED |"; break; case signator::revoked: tmp += " REVOKED |"; break; default: throw SRC_BUG; } tmp += it->fingerprint + "|"; tmp += tools_display_date(it->signing_date) + " |"; ui.message(tmp); if(it->key_validity == signator::expired) { tmp = " |" + tools_display_date(it->signature_expiration_date); ui.message(tmp); } ++it; } ui.printf("------------------+----------------+----------------------------------------+-------------------------+"); ui.printf(" For more information about a key, use the command: gpg --list-key "); } void line_tools_read_from_pipe(shared_ptr & dialog, S_I fd, tlv_list & result) { tuyau tube = tuyau(dialog, fd); result.read(tube); } void line_tools_extract_basename(const char *command_name, string &basename) { basename = path(command_name).basename(); } string::iterator line_tools_find_first_char_of(string &s, unsigned char v) { string::iterator it = s.begin(); while(it != s.end() && *it != v) ++it; return it; } void line_tools_split_path_basename(const char *all, path * &chemin, string & base) { chemin = nullptr; string src = all; string::iterator it = tools_find_last_char_of(src, '/'); if(it != src.end()) // path separator found (pointed to by "it") { it += 1; base = string(it, src.end()); chemin = new (nothrow) path(string(src.begin(), it), true); } else { base = src; chemin = new (nothrow) path("."); } if(chemin == nullptr) throw Ememory("line_tools_split_path_basename"); } void line_tools_split_path_basename(const string & all, string & chemin, string & base) { path *tmp = nullptr; line_tools_split_path_basename(all.c_str(), tmp, base); if(tmp == nullptr) throw SRC_BUG; chemin = tmp->display(); delete tmp; } bool line_tools_split_entrepot_path(const string &all, string & proto, string & login, secu_string & password, string & hostname, string & port, string & path_basename) { bool ret = true; const char *ch = all.c_str(); U_I cursor = 0; U_I ref_cur = 0; U_I tmp; U_I max = all.size(); proto.clear(); login.clear(); password.clear(); hostname.clear(); port.clear(); path_basename.clear(); enum { s_proto, s_login, s_login_escape, s_pass, s_hote, s_port, s_path, s_end } state = s_proto; while(state != s_end && cursor < max) { switch(state) { case s_proto: switch(ch[cursor]) { case ':': ++cursor; if(ch[cursor] != '/') { state = s_end; ret = false; } else { ++cursor; if(ch[cursor] != '/') { state = s_end; ret = false; } else { ++cursor; state = s_login; } } break; case '/': case '@': state = s_end; ret = false; break; default: proto += ch[cursor]; ++cursor; } break; case s_login: switch(ch[cursor]) { case '@': state = s_hote; ++cursor; break; case ':': state = s_pass; ++cursor; ref_cur = cursor; break; case '/': hostname = login; login.clear(); state = s_path; ++cursor; break; case '\\': ++cursor; state = s_login_escape; break; default: login += ch[cursor]; ++cursor; } break; case s_login_escape: login += ch[cursor]; ++cursor; state = s_login; break; case s_pass: switch(ch[cursor]) { case '@': state = s_hote; if(ref_cur > cursor) throw SRC_BUG; tmp = cursor - ref_cur; password.resize(tmp); password.append(ch + ref_cur, tmp); ++cursor; break; case '/': hostname = login; port = string(ch+ref_cur, ch+cursor); login.clear(); password.clear(); state = s_path; ++cursor; break; case ':': state = s_end; ret = false; break; default: ++cursor; } break; case s_hote: switch(ch[cursor]) { case '@': state = s_end; ret = false; break; case '/': state = s_path; ++cursor; break; case ':': state = s_port; ++cursor; break; default: hostname += ch[cursor]; ++cursor; } break; case s_port: switch(ch[cursor]) { case '@': case ':': state = s_end; ret = false; break; case '/': state = s_path; ++cursor; break; default: port += ch[cursor]; ++cursor; } break; case s_path: path_basename += ch[cursor]; ++cursor; break; case s_end: throw SRC_BUG; // while loop should end with that status default: throw SRC_BUG; // unknown status } } // sanity checks if(ret) { if(state != s_path) ret = false; if(hostname.size() == 0) ret = false; if(path_basename.size() == 0) { if(max > 0 && ch[max-1] != '/') ret = false; else path_basename = '/'; } } return ret; } S_I line_tools_str2signed_int(const string & x) { stringstream tmp(x); S_I ret; string residu; if((tmp >> ret).fail()) throw Erange("line_tools_str2string", string(dar_gettext("Invalid number: ")) + x); tmp >> residu; for(U_I i = 0; i < residu.size(); ++i) if(residu[i] != ' ') throw Erange("line_tools_str2string", string(dar_gettext("Invalid number: ")) + x); return ret; } infinint line_tools_convert_date(const string & repres) { enum status { init, year, month, day, hour, min, sec, error, finish }; /// first we define a helper class class scan { public: scan(const tm & now) { etat = init; when = now; when.tm_sec = when.tm_min = when.tm_hour = 0; when.tm_wday = 0; // ignored by mktime when.tm_yday = 0; // ignored by mktime when.tm_isdst = 1; // provided time is local daylight saving time tmp = 0; }; scan(const scan & ref) = default; scan & operator = (const scan & ref) = default; ~scan() = default; status get_etat() const { return etat; }; tm get_struct() const { return when; }; void add_digit(char a) { if(a < 48 || a > 57) // ascii code for zero is 48, for nine is 57 throw SRC_BUG; tmp = tmp*10 + (a-48); }; void set_etat(const status & val) { switch(etat) { case year: if(tmp < 1970) throw Erange("line_tools_convert_date", dar_gettext("date before 1970 is not allowed")); when.tm_year = tmp - 1900; break; case month: if(tmp < 1 || tmp > 12) throw Erange("line_tools_convert_date", dar_gettext("Incorrect month")); when.tm_mon = tmp - 1; break; case day: if(tmp < 1 || tmp > 31) throw Erange("line_tools_convert_date", dar_gettext("Incorrect day of month")); when.tm_mday = tmp; break; case hour: if(tmp < 0 || tmp > 23) throw Erange("line_tools_convert_date", dar_gettext("Incorrect hour")); when.tm_hour = tmp; break; case min: if(tmp < 0 || tmp > 59) throw Erange("line_tools_convert_date", dar_gettext("Incorrect minute")); when.tm_min = tmp; break; case sec: if(tmp < 0 || tmp > 59) throw Erange("line_tools_convert_date", dar_gettext("Incorrect second")); when.tm_sec = tmp; break; case error: throw Erange("line_tools_convert_date", dar_gettext("Bad formatted date expression")); default: break; // nothing to do } tmp = 0; etat = val; }; private: struct tm when; status etat; S_I tmp; }; // then we define local variables time_t now = ::time(nullptr), when; struct tm result; tools_localtime(now, &result); scan scanner = scan(result); U_I c, size = repres.size(), ret; struct tm tmp; // now we parse the string to update the stucture tm "when" // first, determining initial state switch(tools_count_in_string(repres, '/')) { case 0: switch(tools_count_in_string(repres, '-')) { case 0: scanner.set_etat(hour); break; case 1: scanner.set_etat(day); break; default: scanner.set_etat(error); } break; case 1: scanner.set_etat(month); break; case 2: scanner.set_etat(year); break; default: scanner.set_etat(error); } // second, parsing the string for(c = 0; c < size && scanner.get_etat() != error; ++c) switch(repres[c]) { case '/': switch(scanner.get_etat()) { case year: scanner.set_etat(month); break; case month: scanner.set_etat(day); break; default: scanner.set_etat(error); } break; case ':': switch(scanner.get_etat()) { case hour: scanner.set_etat(min); break; case min: scanner.set_etat(sec); break; default: scanner.set_etat(error); } break; case '-': switch(scanner.get_etat()) { case day: scanner.set_etat(hour); break; default: scanner.set_etat(error); } break; case ' ': case '\t': case '\n': case '\r': break; // we ignore spaces, tabs, CR and LF case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': scanner.add_digit(repres[c]); break; default: scanner.set_etat(error); } scanner.set_etat(finish); tmp = scanner.get_struct(); when = mktime(&tmp); if(when > now) throw Erange("line_tools_convert_date", dar_gettext("Given date must be in the past")); ret = when; return ret; } void line_tools_display_features(user_interaction & dialog) { NLS_SWAP_IN; try { const char *endy = nullptr; string time_accuracy = ""; string threadar_version = ""; string curl_version = ""; dialog.printf(gettext(" gzip compression (libz) : %s"), YES_NO(compile_time::libz())); dialog.printf(gettext(" bzip2 compression (libbzip2) : %s"), YES_NO(compile_time::libbz2())); dialog.printf(gettext(" lzo compression (liblzo2) : %s"), YES_NO(compile_time::liblzo())); dialog.printf(gettext(" xz compression (liblzma) : %s"), YES_NO(compile_time::libxz())); dialog.printf(gettext(" zstd compression (libzstd) : %s"), YES_NO(compile_time::libzstd())); dialog.printf(gettext(" lz4 compression (liblz4) : %s"), YES_NO(compile_time::liblz4())); dialog.printf(gettext(" Strong encryption (libgcrypt): %s"), YES_NO(compile_time::libgcrypt())); dialog.printf(gettext(" Public key ciphers (gpgme) : %s"), YES_NO(compile_time::public_key_cipher())); dialog.printf(gettext(" Extended Attributes support : %s"), YES_NO(compile_time::ea())); dialog.printf(gettext(" Large files support (> 2GB) : %s"), YES_NO(compile_time::largefile())); dialog.printf(gettext(" ext2fs NODUMP flag support : %s"), YES_NO(compile_time::nodump())); if(compile_time::bits() == 0) dialog.printf(gettext(" Integer size used : unlimited")); else dialog.printf(gettext(" Integer size used : %d bits"), compile_time::bits()); dialog.printf(gettext(" Thread safe support : %s"), YES_NO(compile_time::thread_safe())); dialog.printf(gettext(" Furtive read mode support : %s"), YES_NO(compile_time::furtive_read())); dialog.printf(gettext(" Linux ext2/3/4 FSA support : %s"), YES_NO(compile_time::FSA_linux_extX())); dialog.printf(gettext(" Mac OS X HFS+ FSA support : %s"), YES_NO(compile_time::FSA_birthtime())); dialog.printf(gettext(" Linux statx() support : %s"), YES_NO(compile_time::Linux_statx())); switch(compile_time::system_endian()) { case compile_time::big: endy = gettext("big"); break; case compile_time::little: endy = gettext("little"); break; case compile_time::error: endy = gettext("error!"); break; default: throw SRC_BUG; } dialog.printf(gettext(" Detected system/CPU endian : %s"), endy); dialog.printf(gettext(" Posix fadvise support : %s"), YES_NO(compile_time::posix_fadvise())); dialog.printf(gettext(" Large dir. speed optimi. : %s"), YES_NO(compile_time::fast_dir())); if(compile_time::nanosecond_read()) time_accuracy = "1 nanosecond"; else { if(compile_time::microsecond_read()) time_accuracy = "1 microsecond"; else time_accuracy = "1 second"; } dialog.printf(gettext(" Timestamp read accuracy : %S"), &time_accuracy); if(compile_time::nanosecond_write()) time_accuracy = "1 nanosecond"; else { if(compile_time::microsecond_write()) time_accuracy = "1 microsecond"; else time_accuracy = "1 second"; } dialog.printf(gettext(" Timestamp write accuracy : %S"), &time_accuracy); dialog.printf(gettext(" Restores dates of symlinks : %s"), YES_NO(compile_time::symlink_restore_dates())); if(compile_time::libthreadar()) threadar_version = string("(") + compile_time::libthreadar_version() + ")"; else threadar_version = ""; dialog.printf(gettext(" Multiple threads (libthreads): %s %s"), YES_NO(compile_time::libthreadar()), threadar_version.c_str()); dialog.printf(gettext(" Delta compression (librsync) : %s"), YES_NO(compile_time::librsync())); if(compile_time::remote_repository()) curl_version = string("(") + compile_time::libcurl_version() + ")"; else curl_version = ""; dialog.printf(gettext(" Remote repository (libcurl) : %s %s"), YES_NO(compile_time::remote_repository()), curl_version.c_str()); dialog.printf(gettext(" argon2 hashing (libargon2) : %s"), YES_NO(compile_time::libargon2())); } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } const char *line_tools_get_from_env(const char **env, const char *clef) { unsigned int index = 0; const char *ret = nullptr; if(env == nullptr || clef == nullptr) return nullptr; while(ret == nullptr && env[index] != nullptr) { unsigned int letter = 0; while(clef[letter] != '\0' && env[index][letter] != '\0' && env[index][letter] != '=' && clef[letter] == env[index][letter]) letter++; if(clef[letter] == '\0' && env[index][letter] == '=') ret = env[index]+letter+1; else index++; } return ret; } void line_tools_check_basename(user_interaction & dialog, const path & loc, string & base, const string & extension, bool create) { NLS_SWAP_IN; try { regular_mask suspect(string(".+\\.[0-9]+\\.")+extension+string("$"), true); regular_mask suspect2(string("\\.0*$"), true); string old_path = (loc.append(base)).display(); // is there a slice available ? if(is_a_slice_available(dialog, old_path, extension)) return; // yes, thus basename is not a mistake // is basename is suspect if(suspect.is_covered(base)) // full slice name { // removing the suspicious end (..extension) // and checking the avaibility of such a slice string new_base = retreive_basename(base, extension); string new_path = (loc.append(new_base)).display(); if(is_a_slice_available(dialog, new_path, extension)) { dialog.message(tools_printf(gettext("Warning, no archive exist with \"%S\" as basename, however a slice part of an archive which basename is \"%S\" exists. Assuming a shell completion occurred and using that basename instead"), &base, &new_base)); base = new_base; } else // no slice exist neither with that slice name { try { if(create) { dialog.pause(tools_printf(gettext("Warning, the provided basename \"%S\" may lead to confusion as it looks like a slice name. It is advise to rather use \"%S\" as basename instead. Do you want to change the basename accordingly?"), &base, &new_base)); base = new_base; } } catch(Euser_abort & e) { dialog.message(tools_printf(gettext("OK, keeping %S as basename"), &base)); } } } else if(suspect2.is_covered(base)) { // shell completed up to the slice number in a file string new_base = retreive_basename2(base); // removing the ending dot eventually followed by zeros string new_path = (loc.append(new_base)).display(); if(is_a_slice_available(dialog, new_path, extension)) { dialog.message(tools_printf(gettext("Warning, no archive exist with \"%S\" as basename, however a slice part of an archive which basename is \"%S\" exists. Assuming a shell completion occurred and using that basename instead"), &base, &new_base)); base = new_base; } } } catch(...) { NLS_SWAP_OUT; throw; } NLS_SWAP_OUT; } void line_tools_check_min_digits(user_interaction & dialog, const path & loc, const string & base, const string & extension, infinint & num_digits) { bool found = false; string cur; etage contents(dialog, loc.display().c_str(), datetime(0), datetime(0), false, false); regular_mask slice = regular_mask(base + "\\.0+[1-9][0-9]*\\." + extension + "$", true); while(!found && contents.read(cur)) { found = slice.is_covered(cur); if(found) { // extract the number of digits from the slice name S_I index_last = cur.find_last_not_of(string(".")+extension); cur = string(cur.begin(), cur.begin() + index_last + 1); S_I index_first = cur.find_last_not_of("0123456789"); if(num_digits.is_zero() && index_last - index_first > 1) { num_digits = index_last - index_first; dialog.printf(gettext("Auto detecting min-digits to be %i"), &num_digits); } } } } string line_tools_getcwd() { const U_I step = 1024; U_I length = step; char *buffer = nullptr, *ret; string cwd; try { do { buffer = new (nothrow) char[length]; if(buffer == nullptr) throw Ememory("line_tools_getcwd()"); ret = getcwd(buffer, length-1); // length-1 to keep a place for ending '\0' if(ret == nullptr) // could not get the CWD { if(errno == ERANGE) // buffer too small { delete [] buffer; buffer = nullptr; length += step; } else // other error throw Erange("line_tools_getcwd", string(dar_gettext("Cannot get full path of current working directory: ")) + tools_strerror_r(errno)); } } while(ret == nullptr); buffer[length - 1] = '\0'; cwd = buffer; } catch(...) { if(buffer != nullptr) delete [] buffer; throw; } if(buffer != nullptr) delete [] buffer; return cwd; } void line_tools_read_range(const string & s, S_I & min, U_I & max) { string::const_iterator it = s.begin(); while(it < s.end() && *it != '-') it++; try { if(it < s.end()) { min = tools_str2int(string(s.begin(), it)); max = tools_str2int(string(++it, s.end())); } else min = max = tools_str2int(s); } catch(Erange & e) { min = line_tools_str2signed_int(s); max = 0; } } string line_tools_build_regex_for_exclude_mask(const string & prefix, const string & relative_part) { string result = "^"; string::const_iterator it = prefix.begin(); // prepending any non alpha numeric char of the root by a anti-slash for( ; it != prefix.end() ; ++it) { if(isalnum(*it) || *it == '/' || *it == ' ') result += *it; else { result += '\\'; result += *it; } } // adding a trailing / if necessary string::reverse_iterator tr = result.rbegin(); if(tr == result.rend() || *tr != '/') result += '/'; // adapting and adding the relative_part it = relative_part.begin(); if(it != relative_part.end() && *it == '^') it++; // skipping leading ^ else result += ".*"; // prepending wilde card sub-expression for( ; it != relative_part.end() && *it != '$' ; ++it) result += *it; result += "(/.+)?$"; return result; } string line_tools_get_euid() { string ret; uid_t uid = geteuid(); libdar::deci conv = infinint(uid); ret += tools_name_of_uid(uid) + "("+ conv.human() + ")"; return ret; } string line_tools_get_egid() { string ret; uid_t gid = getegid(); libdar::deci conv = infinint(gid); ret += tools_name_of_gid(gid) + "("+ conv.human() + ")"; return ret; } string line_tools_get_hostname() { string ret; struct utsname uts; if(uname(&uts) < 0) throw Erange("line_tools_get_hostname", string(dar_gettext("Error while fetching hostname: ")) + tools_strerror_r(errno)); ret = string(uts.nodename); return ret; } string line_tools_get_date_utc() { string ret; datetime now = datetime(::time(nullptr), 0, datetime::tu_second); ret = tools_display_date(now); return ret; } void line_tools_merge_to_deque(deque & a, const deque & b) { deque::const_iterator ptrb = b.begin(); while(ptrb != b.end()) { deque::const_iterator ptra = a.begin(); while(ptra != a.end() && *ptra != *ptrb) ++ptra; if(ptra == a.end()) a.push_back(*ptrb); ++ptrb; } } deque line_tools_substract_from_deque(const deque & a, const deque & b) { deque ret; deque::const_iterator ptra = a.begin(); while(ptra != a.end()) { deque::const_iterator ptrb = b.begin(); while(ptrb != b.end() && *ptra != *ptrb) ++ptrb; if(ptrb == b.end()) ret.push_back(*ptra); ++ptra; } return ret; } delta_sig_block_size::fs_function_t line_tools_string_to_sig_block_size_function(const std::string & funname) { if(funname == "fixed") return delta_sig_block_size::fixed; if(funname == "linear") return delta_sig_block_size::linear; if(funname == "log2") return delta_sig_block_size::log2; if(funname == "square2") return delta_sig_block_size::square2; if(funname == "square3") return delta_sig_block_size::square3; throw Erange("line_tools_string_to_sig_block_function", gettext("unknown name give for delta signature block len function")); } void line_tools_split_compression_algo(const char *arg, U_I base, compression & algo, U_I & level, U_I & block_size) { if(arg == nullptr) throw SRC_BUG; else { string working = arg; deque split; infinint tmp; line_tools_split(working, ':', split); switch(split.size()) { case 1: if(!tools_my_atoi(working.c_str(), level)) { // argument to -z is not an integer, testing whether this is an algorithm try { algo = string2compression(working.c_str()); level = 9; // argument is a compression algo, level is 9 by default } catch(Erange & e) { throw Erange("split_compression_algo", tools_printf(gettext("%s does not name a compression \"[algorithm][:][level]\" , like for examples \"gzip\", \"lzo\", \"bzip2\", \"lzo:3\", \"gzip:2\", \"8\" or \"1\". Please review the man page about -z option"), working.c_str())); } } else // argument is a compression level, algorithm is gzip by default algo = compression::gzip; block_size = 0; break; case 2: if(split[0] != "") algo = string2compression(split[0]); else algo = compression::gzip; // default algorithm if(split[1] != "") { if(!tools_my_atoi(split[1].c_str(), level) || (level > 9 && algo != compression::zstd) || level < 1) throw Erange("split_compression_algo", gettext("Compression level must be between 1 and 9, included")); } else level = 9; // default compression level block_size = 0; break; case 3: if(split[0] != "") algo = string2compression(split[0]); else algo = compression::gzip; if(split[1] != "") { if(!tools_my_atoi(split[1].c_str(), level) || (level > 9 && algo != compression::zstd) || level < 1) throw Erange("split_compression_algo", gettext("Compression level must be between 1 and 9, included")); } else level = 9; tmp = tools_get_extended_size(split[2], base); block_size = 0; tmp.unstack(block_size); if(!tmp.is_zero()) throw Erange("split_compression_algo", gettext("Compression block size too large for this operating system")); break; default: throw Erange("split_compression_algo", gettext("invalid argument given for compression scheme")); } } } /////////////////// static string build(string::iterator a, string::iterator b) { string ret = ""; while(a != b) ret += *a++; return ret; } static bool is_a_slice_available(user_interaction & ui, const string & base, const string & extension) { path *chem = nullptr; bool ret = false; try { string rest; line_tools_split_path_basename(base.c_str(), chem, rest); try { etage contents = etage(ui, chem->display().c_str(), datetime(0), datetime(0), false, false); // we don't care the dates here so we set them to zero regular_mask slice = regular_mask(rest + "\\.[0-9]+\\."+ extension, true); while(!ret && contents.read(rest)) ret = slice.is_covered(rest); } catch(Erange & e) { ret = false; } } catch(...) { if(chem != nullptr) delete chem; throw; } if(chem != nullptr) delete chem; return ret; } static string retreive_basename(const string & base, const string & extension) { string new_base = base; S_I index; if(new_base.size() < 2+1+extension.size()) throw SRC_BUG; // must be a slice filename index = new_base.find_last_not_of(string(".")+extension); new_base = string(new_base.begin(), new_base.begin()+index); index = new_base.find_last_not_of("0123456789"); new_base = string(new_base.begin(), new_base.begin()+index); return new_base; } static string retreive_basename2(const string & base) { string new_base = base; S_I index; index = new_base.find_last_not_of(string("0")); // this will point before the last dot preceeding the zeros new_base = string(new_base.begin(), new_base.begin()+index); return new_base; } static void tools_localtime(const time_t & timep, struct tm *result) { #if HAVE_LOCALTIME_R struct tm *ret = localtime_r(&timep, result); if(ret == nullptr) { string err = tools_strerror_r(errno); throw Erange("tools_localtime", tools_printf(gettext("Error met while retrieving current time: %S"), &err)); } #else struct tm *ret = localtime(&timep); if(ret == nullptr) { string err = tools_strerror_r(errno); throw Erange("tools_localtime", tools_printf(gettext("Error met while retrieving current time: %S"), &err)); } *result = *ret; #endif } dar-2.7.15/src/dar_suite/config_file.hpp0000644000175000017500000000377014636066467015051 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file config_file.hpp /// \brief specific routines to manages included files's targets /// \ingroup CMDLINE #ifndef CONFIG_FILE_HPP #define CONFIG_FILE_HPP #include "../my_config.h" #include #include "hide_file.hpp" using namespace libdar; /// \addtogroup CMDLINE /// @{ class config_file : public hide_file { public: config_file(const deque & target, generic_file &f); config_file(const config_file & ref) = default; config_file & operator = (const config_file & ref) = default; ~config_file() = default; deque get_read_targets() const; protected: virtual void fill_morceau() override; private: struct t_cible { string target; bool seen; t_cible(const string & val) { target = val; seen = false; }; t_cible() { target = ""; seen = false; }; }; deque cibles; bool is_a_target(const string & val); bool find_next_target(generic_file &f, infinint & debut, string & nature, infinint & fin); }; /// @} #endif dar-2.7.15/src/dar_suite/getopt_decision.h0000644000175000017500000000320714636066467015417 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ /// \file getopt_decision.h /// \brief switch routine to define which file to include based on the result of the configure script /// \ingroup CMDLINE #ifndef GETOPT_DECISION_H #define GETOPT_DECISION_H #include "../my_config.h" #if HAVE_GETOPT_LONG #if HAVE_GETOPT_IN_UNISTD_H #if HAVE_GETOPT_LONG_IN_UNISTD_H // we do not need to include #else // we miss getopt_long but have getopt #include "my_getopt_long.h" #endif #else // no getopt() in unistd.h // we assume that if unistd.h does not know getopt() it does nor know getopt_long() #include #endif #endif #ifndef EOF #define EOF -1 #endif #endif dar-2.7.15/src/python/0000755000175000017500000000000014640025216011465 500000000000000dar-2.7.15/src/python/Makefile.in0000644000175000017500000004435114640025156013464 00000000000000# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/python ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(dist_pkgdata_DATA) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkgdatadir)" DATA = $(dist_pkgdata_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CXXSTDFLAGS = @CXXSTDFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN_PROG = @DOXYGEN_PROG@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GPGME_CFLAGS = @GPGME_CFLAGS@ GPGME_CONFIG = @GPGME_CONFIG@ GPGME_LIBS = @GPGME_LIBS@ GPGRT_CONFIG = @GPGRT_CONFIG@ GREP = @GREP@ HAS_DOT = @HAS_DOT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBCURL_CFLAGS = @LIBCURL_CFLAGS@ LIBCURL_LIBS = @LIBCURL_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTHREADAR_CFLAGS = @LIBTHREADAR_CFLAGS@ LIBTHREADAR_LIBS = @LIBTHREADAR_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGMERGE = @MSGMERGE@ MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POSUB = @POSUB@ PYEXT = @PYEXT@ PYFLAGS = @PYFLAGS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ UPX_PROG = @UPX_PROG@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dot = @dot@ doxygen = @doxygen@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ groff = @groff@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ tmp = @tmp@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ upx = @upx@ @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@MYLIB = libdar.so @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@MYLIB = libdar64.so @BUILD_MODE32_TRUE@MYLIB = libdar32.so @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@AM_CPPFLAGS = -fPIC -I'../..' -I'$(srcdir)/..' -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) $(PYFLAGS) -fPIC @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@AM_CPPFLAGS = -fPIC -DLIBDAR_MODE=64 -I'../..' -I'$(srcdir)/..' -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) $(PYFLAGS) @BUILD_MODE32_TRUE@AM_CPPFLAGS = -fPIC -DLIBDAR_MODE=32 -I'../..' -I'$(srcdir)/..' -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) $(PYFLAGS) dist_pkgdata_DATA = pybind11_libdar.cpp @PYTHON_BINDING_FALSE@TARGET = @PYTHON_BINDING_TRUE@TARGET = libdar$(PYEXT) AM_LDFLAGS = -shared -Wl,--no-as-needed ../libdar/.libs/$(MYLIB) all: all-am .SUFFIXES: .SUFFIXES: .cpp .o $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/python/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/python/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-dist_pkgdataDATA: $(dist_pkgdata_DATA) @$(NORMAL_INSTALL) @list='$(dist_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgdatadir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgdatadir)" || exit $$?; \ done uninstall-dist_pkgdataDATA: @$(NORMAL_UNINSTALL) @list='$(dist_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) all-local installdirs: for dir in "$(DESTDIR)$(pkgdatadir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." @PYTHON_BINDING_FALSE@install-data-hook: @PYTHON_BINDING_FALSE@uninstall-hook: clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dist_pkgdataDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dist_pkgdataDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) uninstall-hook .MAKE: install-am install-data-am install-strip uninstall-am .PHONY: all all-am all-local check check-am clean clean-generic \ clean-libtool clean-local cscopelist-am ctags-am distclean \ distclean-generic distclean-libtool distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-data-hook install-dist_pkgdataDATA \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags-am uninstall uninstall-am \ uninstall-dist_pkgdataDATA uninstall-hook .PRECIOUS: Makefile @PYTHON_BINDING_TRUE@install-data-hook: $(TARGET) @PYTHON_BINDING_TRUE@ $(INSTALL) -d $(DESTDIR)$(libdir)/python3/dist-packages @PYTHON_BINDING_TRUE@ $(INSTALL) -m 0644 $(TARGET) $(DESTDIR)$(libdir)/python3/dist-packages @PYTHON_BINDING_TRUE@uninstall-hook: @PYTHON_BINDING_TRUE@ rm -rf $(DESTDIR)$(libdir)/python3/dist-packages/$(TARGET) @PYTHON_BINDING_TRUE@ rmdir -p $(DESTDIR)$(libdir)/python3/dist-packages 2> /dev/null || echo "" all-local: $(TARGET) .cpp.o: $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(AM_CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $< -o $@ libdar$(PYEXT): pybind11_libdar.o $(CXX) $(AM_LDFLAGS) $< -o $@ clean-local: rm -f libdar$(PYEXT) *.o # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dar-2.7.15/src/python/Makefile.am0000644000175000017500000000447614636066467013476 00000000000000####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### if BUILD_MODE32 MYLIB=libdar32.so AM_CPPFLAGS=-fPIC -DLIBDAR_MODE=32 -I'../..' -I'$(srcdir)/..' -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) $(PYFLAGS) else if BUILD_MODE64 MYLIB=libdar64.so AM_CPPFLAGS=-fPIC -DLIBDAR_MODE=64 -I'../..' -I'$(srcdir)/..' -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) $(PYFLAGS) else MYLIB=libdar.so AM_CPPFLAGS=-fPIC -I'../..' -I'$(srcdir)/..' -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) $(PYFLAGS) -fPIC endif endif dist_pkgdata_DATA = pybind11_libdar.cpp if PYTHON_BINDING TARGET=libdar$(PYEXT) install-data-hook: $(TARGET) $(INSTALL) -d $(DESTDIR)$(libdir)/python3/dist-packages $(INSTALL) -m 0644 $(TARGET) $(DESTDIR)$(libdir)/python3/dist-packages uninstall-hook: rm -rf $(DESTDIR)$(libdir)/python3/dist-packages/$(TARGET) rmdir -p $(DESTDIR)$(libdir)/python3/dist-packages 2> /dev/null || echo "" else TARGET= endif AM_LDFLAGS = -shared -Wl,--no-as-needed ../libdar/.libs/$(MYLIB) all-local: $(TARGET) .cpp.o: $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(AM_CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $< -o $@ libdar$(PYEXT): pybind11_libdar.o $(CXX) $(AM_LDFLAGS) $< -o $@ clean-local: rm -f libdar$(PYEXT) *.o dar-2.7.15/src/python/pybind11_libdar.cpp0000644000175000017500000026770214636067146015110 00000000000000/*********************************************************************/ // dar - disk archive - a backup/restoration program // Copyright (C) 2002-2024 Denis Corbin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // to contact the author, see the AUTHOR file /*********************************************************************/ #include #include #include #include // this should be passed from Makefile #define LIBDAR_MODE 64 #define HAVE_CONFIG_H 1 // #include "../my_config.h" #include "../libdar/libdar.hpp" #include "../libdar/tools.hpp" #include "../libdar/cat_nomme.hpp" #include "../libdar/entrepot_local.hpp" #include "../libdar/entrepot_libcurl.hpp" PYBIND11_MODULE(libdar, mod) { // mod is of type pybind11::module mod.doc() = "libdar python binding"; /////////////////////////////////////////// // get_version // mod.def("get_version", [](bool libgcrypt, bool gpgme){ libdar::U_I maj, med, min; libdar::get_version(maj, med, min, libgcrypt, gpgme); return std::make_tuple(maj,med,min); }, pybind11::arg("init_libgcrypt") = true, pybind11::arg("init_gpgme") = true); mod.def("close_and_clean", &libdar::close_and_clean); #ifdef MUTEX_WORKS mod .def("cancel_thread", &libdar::cancel_thread) .def("cancel_status", &libdar::cancel_status) .def("cancel_clear", &libdar::cancel_clear) .def("get_thread_count", &libdar::get_thread_count); #endif /////////////////////////////////////////// // exceptions classes (from erreurs.hpp) // ////// // the following is useless as pybind11 // cannot provide a class description // when this one is used as exception /* class pyEgeneric_pub : public libdar::Egeneric { public: using libdar::Egeneric::Egeneric; using libdar::Egeneric::exceptionID; }; class pyEgeneric : public pyEgeneric_pub { public: using pyEgeneric_pub::pyEgeneric_pub; virtual std::string exceptionID() const override { PYBIND11_OVERRIDE_PURE( std::string, // return type pyEgeneric_pub, // parent class exceptionID, // name of the method in C++ (must match Python name) ); // trailing comma needed for portability }; }; pybind11::class_ egeneric(mod, "Egeneric"); egeneric .def(pybind11::init()) .def("get_message", &pyEgeneric_pub::get_message) .def("dump_str", &pyEgeneric_pub::dump_str) .def("exceptionID", &pyEgeneric_pub::exceptionID); // the following makes a conflict on symboc Egeneric in python // as the symbol is already used from the py::class_ directive above // static pybind11::exception exc_Egeneric(mod, "Egeneric"); pybind11::class_(mod, "Ememory", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Esecu_memory") .def(pybind11::init()); pybind11::class_(mod, "Ebug", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Einfinint", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Elimitint", egeneric) .def(pybind11::init<>()); pybind11::class_(mod, "Erange", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Edeci", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Efeature", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Ehardware", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Euser_abort", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Edata", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Escript", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Elibcall", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Ecompilation", egeneric) .def(pybind11::init()); pybind11::class_(mod, "Ethread_cancel", egeneric) .def(pybind11::init()); pybind11::class_ esystem(mod, "Esystem", egeneric); pybind11::enum_(esystem, "io_error") .value("io_exist", libdar::Esystem::io_exist) .value("io_absent", libdar::Esystem::io_absent) .value("io_access", libdar::Esystem::io_access) .value("io_ro_fs", libdar::Esystem::io_ro_fs) .export_values(); esystem.def(pybind11::init()) .def("get_code", &libdar::Esystem::get_code); pybind11::class_(mod, "Enet_auth", egeneric) .def(pybind11::init()); */ // for the reason stated above we will use // a new class toward which we will translate libdar // exception class darexc : public std::exception { public: darexc(const std::string & mesg): x_mesg(mesg) {}; virtual const char * what() const noexcept override { return x_mesg.c_str(); }; private: std::string x_mesg; }; // this will only expose the what() method // to python side. This is better than nothing pybind11::register_exception(mod, "darexc"); // now the translation from libdar::Egeneric to darexc pybind11::register_exception_translator([](std::exception_ptr p) { try { if(p) std::rethrow_exception(p); } catch(libdar::Efeature & e) { throw darexc(std::string(gettext("NOT YET IMPLEMENTED FEATURE has been used: ")) + e.get_message()); } catch(libdar::Ehardware & e) { throw darexc(std::string(gettext("SEEMS TO BE A HARDWARE PROBLEM: ")) + e.get_message()); } catch(libdar::Esecu_memory & e) { throw darexc(std::string(gettext("Lack of SECURED memory to achieve the operation, aborting operation"))); } catch(libdar::Ememory & e) { throw darexc(std::string(gettext("Lack of memory to achieve the operation, aborting operation"))); } catch(std::bad_alloc & e) { throw darexc(std::string(gettext("Lack of memory to achieve the operation, aborting operation"))); } catch(libdar::Erange & e) { throw darexc(std::string(gettext("FATAL error, aborting operation: ")) + e.get_message()); } catch(libdar::Euser_abort & e) { throw darexc(std::string(gettext("Aborting program. User refused to continue while asking: ")) + e.get_message()); } catch(libdar::Ethread_cancel & e) { throw darexc(std::string(gettext("Program has been aborted for the following reason: ")) + e.get_message()); } catch(libdar::Edata & e) { throw darexc(e.get_message()); } catch(libdar::Escript & e) { throw darexc(std::string(gettext("Aborting program. An error occurred concerning user command execution: ")) + e.get_message()); } catch(libdar::Elibcall & e) { throw darexc(std::string(gettext("Aborting program. An error occurred while calling libdar: ")) + e.get_message()); } catch(libdar::Einfinint & e) { throw darexc(std::string(gettext("Aborting program. ")) + e.get_message()); } catch(libdar::Elimitint & e) { throw darexc(std::string(gettext("Aborting program. ")) + e.get_message()); } catch(libdar::Ecompilation & e) { throw darexc(std::string(gettext("Aborting program. The requested operation needs a feature that has been disabled at compilation time: ")) + e.get_message()); } catch(libdar::Esystem & e) { throw darexc(std::string(gettext("FATAL error from operating system, aborting operation: ")) + e.get_message()); } catch(libdar::Enet_auth & e) { throw darexc(std::string(gettext("FATAL error during network communication, aborting operation: ")) + e.get_message()); } catch(libdar::Ebug & e) { throw darexc(e.dump_str().c_str()); } catch(libdar::Egeneric & e) { throw darexc(e.get_message().c_str()); } } ); /////////////////////////////////////////// // class path (from path.hpp) // pybind11::class_(mod, "path") .def(pybind11::init(), pybind11::arg("s"), pybind11::arg("undisclosed") = false) .def(pybind11::init()) .def("basename", &libdar::path::basename) .def("reset_read", &libdar::path::reset_read) .def("read_subdir", [](libdar::path const & self) { std::string val; bool ret = self.read_subdir(val); return std::make_tuple(ret, val);}) .def("is_relative", &libdar::path::is_relative) .def("is_absolute", &libdar::path::is_absolute) .def("is_undisclosed", &libdar::path::is_undisclosed) .def("pop", [](libdar::path & self) { std::string x; bool ret = self.pop(x); return make_tuple(ret, x);}) .def("pop_front", [](libdar::path & self) { std::string x; bool ret = self.pop_front(x); return make_tuple(ret, x);}) .def("append", &libdar::path::append) .def(pybind11::self + pybind11::self) // operator + libdar::pat .def(pybind11::self + std::string()) // operator + std::string .def(pybind11::self += pybind11::self) // operator += libdar::path .def(pybind11::self += std::string()) // operator += std::string .def("is_subdir_of", &libdar::path::is_subdir_of) .def("display", &libdar::path::display) .def("display_without_root", &libdar::path::display_without_root) .def("degre", &libdar::path::degre) .def("explode_undisclosed", &libdar::path::explode_undisclosed); /////////////////////////////////////////// // infinint (from real_infinint.hpp / limitint.hpp) // #ifndef LIBDAR_MODE pybind11::class_(mod, "infininit") .def(pybind11::init(), pybind11::arg("a") = 0) .def(pybind11::init(), pybind11::arg("a") = 0) .def(pybind11::init(), pybind11::arg("a") = 0) .def(pybind11::init()) .def("dump", &libdar::real_infinint::dump) .def("read", &libdar::real_infinint::read) .def(pybind11::self += pybind11::self) .def(pybind11::self -= pybind11::self) .def(pybind11::self *= pybind11::self) .def(pybind11::self /= pybind11::self) .def(pybind11::self &= pybind11::self) .def(pybind11::self |= pybind11::self) .def(pybind11::self ^= pybind11::self) .def(pybind11::self >>= pybind11::self) .def(pybind11::self <<= pybind11::self) .def(pybind11::self %= pybind11::self) .def(pybind11::self % libdar::U_32()) .def(pybind11::self < pybind11::self) .def(pybind11::self == pybind11::self) .def(pybind11::self > pybind11::self) .def(pybind11::self <= pybind11::self) .def(pybind11::self != pybind11::self) .def(pybind11::self >= pybind11::self) .def(pybind11::self + pybind11::self) .def(pybind11::self - pybind11::self) .def(pybind11::self * pybind11::self) .def(pybind11::self / pybind11::self) .def("is_zero", &libdar::real_infinint::is_zero); mod.def("euclide", [](const libdar::infinint & a, const libdar::infinint & b) { libdar::infinint q, r; euclide(a, b, q, r); return std::make_tuple(q, r); }); #else pybind11::class_ >(mod, "infinint") .def(pybind11::init(), pybind11::arg("a") = 0) .def(pybind11::init(), pybind11::arg("a") = 0) .def(pybind11::init(), pybind11::arg("a") = 0) .def(pybind11::init &>()) .def("dump", &libdar::limitint::dump) .def("read", &libdar::limitint::read) .def(pybind11::self += pybind11::self) .def(pybind11::self -= pybind11::self) .def(pybind11::self *= pybind11::self) .def(pybind11::self /= pybind11::self) .def(pybind11::self &= pybind11::self) .def(pybind11::self |= pybind11::self) .def(pybind11::self ^= pybind11::self) .def(pybind11::self >>= pybind11::self) .def(pybind11::self <<= pybind11::self) .def(pybind11::self %= pybind11::self) .def(pybind11::self % libdar::U_32()) .def(pybind11::self < pybind11::self) .def(pybind11::self == pybind11::self) .def(pybind11::self > pybind11::self) .def(pybind11::self <= pybind11::self) .def(pybind11::self != pybind11::self) .def(pybind11::self >= pybind11::self) .def(pybind11::self + pybind11::self) .def(pybind11::self - pybind11::self) .def(pybind11::self * pybind11::self) .def(pybind11::self / pybind11::self) .def("is_zero", &libdar::limitint::is_zero); mod.def("euclide", [](const libdar::infinint & a, const libdar::infinint & b) { libdar::infinint q, r; euclide(a, b, q, r); return std::make_tuple(q, r); }); #endif /////////////////////////////////////////// // class deci (from deci.hpp / limitint.hpp) // pybind11::class_(mod, "deci") .def(pybind11::init()) .def(pybind11::init()) .def("computer", &libdar::deci::computer) .def("human", &libdar::deci::human); /////////////////////////////////////////// // tools routines related to infinint // mod.def("tools_display_interger_in_metric_system", &libdar::tools_display_integer_in_metric_system, pybind11::arg("number"), pybind11::arg("unit"), pybind11::arg("binary Ki (else K)")); mod.def("tools_get_extended_size", &libdar::tools_get_extended_size, pybind11::arg("input numerical string with unit suffix"), pybind11::arg("unit base: 1000 for SI, 1024 for computer science")); /////////////////////////////////////////// // class statistics // pybind11::class_ >(mod, "statistics") .def(pybind11::init(), pybind11::arg("lock") = true) .def("clear", &libdar::statistics::clear) .def("total", &libdar::statistics::total) .def("get_treated", &libdar::statistics::get_treated) .def("get_hard_links", &libdar::statistics::get_hard_links) .def("get_skipped", &libdar::statistics::get_skipped) .def("get_inode_only", &libdar::statistics::get_inode_only) .def("get_ignored", &libdar::statistics::get_ignored) .def("get_tooold", &libdar::statistics::get_tooold) .def("get_errored", &libdar::statistics::get_errored) .def("get_deleted", &libdar::statistics::get_deleted) .def("get_ea_treated", &libdar::statistics::get_ea_treated) .def("get_byte_amount", &libdar::statistics::get_byte_amount) .def("get_fsa_treated", &libdar::statistics::get_fsa_treated) // .def("get_treated_str", &libdar::statistics::get_treated_str) .def("get_hard_links_str", &libdar::statistics::get_hard_links_str) .def("get_skipped_str", &libdar::statistics::get_skipped_str) .def("get_inode_only_str", &libdar::statistics::get_inode_only_str) .def("get_ignored_str", &libdar::statistics::get_ignored_str) .def("get_tooold_str", &libdar::statistics::get_tooold_str) .def("get_errored_str", &libdar::statistics::get_errored_str) .def("get_deleted_str", &libdar::statistics::get_deleted_str) .def("get_ea_treated_str", &libdar::statistics::get_ea_treated_str) .def("get_byte_amount_str", &libdar::statistics::get_byte_amount_str) .def("get_fsa_treated_str", &libdar::statistics::get_fsa_treated_str); /////////////////////////////////////////// // data structures in crypto.hpp // pybind11::enum_(mod, "crypto_algo") .value("none", libdar::crypto_algo::none) .value("scrambling", libdar::crypto_algo::scrambling) .value("blowfish", libdar::crypto_algo::blowfish) .value("aes256", libdar::crypto_algo::aes256) .value("twofish256", libdar::crypto_algo::twofish256) .value("serpent256", libdar::crypto_algo::serpent256) .value("camellia256", libdar::crypto_algo::camellia256) .export_values(); pybind11::class_ pysignator(mod, "signator"); pybind11::enum_(pysignator, "result_t") .value("good", libdar::signator::good) .value("bad", libdar::signator::bad) .value("unknown_key", libdar::signator::unknown_key) .value("error", libdar::signator::error) .export_values(); pybind11::enum_(pysignator, "key_validity_t") .value("valid", libdar::signator::valid) .value("expired", libdar::signator::expired) .value("revoked", libdar::signator::revoked) .export_values(); pysignator .def_readonly("result", &libdar::signator::result) .def_readonly("key_validity", &libdar::signator::key_validity) .def_readonly("fingerprint", &libdar::signator::fingerprint) .def_readonly("signing_date", &libdar::signator::signing_date) .def_readonly("signature_expiration_date", &libdar::signator::signature_expiration_date) .def(pybind11::self < pybind11::self) .def(pybind11::self == pybind11::self); mod.def("crypto_algo_2_string", &libdar::crypto_algo_2_string, pybind11::arg("algo")); mod.def("same_signatories", &libdar::same_signatories); /////////////////////////////////////////// // masks_* classes // // trampoline class needed to address pure virtual methods // making a template of the trampoline to be able to derive // inherited class from libdar::mask in python environment class pymask : public libdar::mask { public: // using the same constructors using libdar::mask::mask; virtual bool is_covered(const std::string & expression) const override { PYBIND11_OVERRIDE_PURE( bool, libdar::mask, is_covered, expression); }; virtual bool is_covered(const libdar::path & chemin) const override { PYBIND11_OVERRIDE( bool, libdar::mask, is_covered, chemin); }; virtual std::string dump(const std::string & prefix) const override { PYBIND11_OVERRIDE_PURE( std::string, libdar::mask, dump, prefix); }; virtual libdar::mask *clone() const override { PYBIND11_OVERRIDE_PURE( libdar::mask *, libdar::mask, clone, // trailing comma needed for portability ); }; }; // the trampoline for libdar::mask pybind11::class_(mod, "mask") .def(pybind11::init<>()) .def("is_covered", (bool (libdar::mask::*)(const std::string &) const) &libdar::mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::mask::*)(const libdar::path &) const) &libdar::mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::mask::dump) .def("clone", &libdar::mask::clone); // inhertied class from libdar::mask will not have their virtual method overridable in python // only class libdar::mask can be that way inherited. // doing that way is simple. It does not worth effort to make things more complicated as there is no reason // to inherit from existing inherited libdar::mask classes pybind11::class_(mod, "bool_mask") .def(pybind11::init()) .def("is_covered", (bool (libdar::bool_mask::*)(const std::string &) const) &libdar::bool_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::bool_mask::*)(const libdar::path &) const) &libdar::bool_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::bool_mask::dump) .def("clone", &libdar::bool_mask::clone); pybind11::class_(mod, "simple_mask") .def(pybind11::init()) .def("is_covered", (bool (libdar::simple_mask::*)(const std::string &) const) &libdar::simple_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::simple_mask::*)(const libdar::path &) const) &libdar::simple_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::simple_mask::dump) .def("clone", &libdar::simple_mask::clone); pybind11::class_(mod, "regular_mask") .def(pybind11::init()) .def("is_covered", (bool (libdar::regular_mask::*)(const std::string &) const) &libdar::regular_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::regular_mask::*)(const libdar::path &) const) &libdar::regular_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::regular_mask::dump) .def("clone", &libdar::regular_mask::clone); pybind11::class_(mod, "not_mask") .def(pybind11::init()) .def("is_covered", (bool (libdar::not_mask::*)(const std::string &) const) &libdar::not_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::not_mask::*)(const libdar::path &) const) &libdar::not_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::not_mask::dump) .def("clone", &libdar::not_mask::clone); pybind11::class_(mod, "et_mask") .def(pybind11::init<>()) .def("is_covered", (bool (libdar::et_mask::*)(const std::string &) const) &libdar::et_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::et_mask::*)(const libdar::path &) const) &libdar::et_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::et_mask::dump) .def("clone", &libdar::et_mask::clone) .def("add_mask", &libdar::et_mask::add_mask) .def("size", &libdar::et_mask::size) .def("clear", &libdar::et_mask::clear); pybind11::class_(mod, "ou_mask") .def(pybind11::init<>()) .def("is_covered", (bool (libdar::ou_mask::*)(const std::string &) const) &libdar::ou_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::ou_mask::*)(const libdar::path &) const) &libdar::ou_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::ou_mask::dump) .def("clone", &libdar::ou_mask::clone) .def("add_mask", &libdar::ou_mask::add_mask) .def("size", &libdar::ou_mask::size) .def("clear", &libdar::ou_mask::clear); pybind11::class_(mod, "simple_path_mask") .def(pybind11::init()) .def("is_covered", (bool (libdar::simple_path_mask::*)(const std::string &) const) &libdar::simple_path_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::simple_path_mask::*)(const libdar::path &) const) &libdar::simple_path_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::simple_path_mask::dump) .def("clone", &libdar::simple_path_mask::clone); pybind11::class_(mod, "same_path_mask") .def(pybind11::init()) .def("is_covered", (bool (libdar::same_path_mask::*)(const std::string &) const) &libdar::same_path_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::same_path_mask::*)(const libdar::path &) const) &libdar::same_path_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::same_path_mask::dump) .def("clone", &libdar::same_path_mask::clone); pybind11::class_(mod, "exclude_dir_mask") .def(pybind11::init()) .def("is_covered", (bool (libdar::exclude_dir_mask::*)(const std::string &) const) &libdar::exclude_dir_mask::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::exclude_dir_mask::*)(const libdar::path &) const) &libdar::exclude_dir_mask::is_covered, "Mask based on libdar::path") .def("dump", &libdar::exclude_dir_mask::dump) .def("clone", &libdar::exclude_dir_mask::clone); /////////////////////////////////////////// // mask_list_* class // pybind11::class_(mod, "mask_list") .def(pybind11::init()) .def("is_covered", (bool (libdar::mask_list::*)(const std::string &) const) &libdar::mask_list::is_covered, "Mask based on string") .def("is_covered", (bool (libdar::mask_list::*)(const libdar::path &) const) &libdar::mask_list::is_covered, "Mask based on libdar::path") .def("dump", &libdar::mask_list::dump) .def("clone", &libdar::mask_list::clone) .def("size", &libdar::mask_list::size); /////////////////////////////////////////// // binding for what's criterium.hpp // class pycriterium : public libdar::criterium { public: virtual bool evaluate(const libdar::cat_nomme & first, const libdar::cat_nomme & second) const override { PYBIND11_OVERRIDE_PURE(bool, libdar::criterium, evaluate, first, second); } virtual criterium *clone() const override { PYBIND11_OVERRIDE_PURE(libdar::criterium *, libdar::criterium, clone,); // trailing comma expected as this method has no argument }; }; pybind11::class_(mod, "criterium") .def(pybind11::init<>()) .def("evaluate", &libdar::criterium::evaluate) .def("clone", &libdar::criterium::clone); // inherited class from libdar::criterium pybind11::class_(mod, "crit_in_place_is_inode") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_is_inode::evaluate) .def("clone", &libdar::crit_in_place_is_inode::clone); pybind11::class_(mod, "crit_in_place_is_dir") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_is_dir::evaluate) .def("clone", &libdar::crit_in_place_is_dir::clone); pybind11::class_(mod, "crit_in_place_is_file") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_is_file::evaluate) .def("clone", &libdar::crit_in_place_is_file::clone); pybind11::class_(mod, "crit_in_place_is_hardlinked_inode") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_is_hardlinked_inode::evaluate) .def("clone", &libdar::crit_in_place_is_hardlinked_inode::clone); pybind11::class_(mod, "crit_in_place_is_new_hardlinked_inode") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_is_new_hardlinked_inode::evaluate) .def("clone", &libdar::crit_in_place_is_new_hardlinked_inode::clone); pybind11::class_(mod, "crit_in_place_data_more_recent") .def(pybind11::init(), pybind11::arg("hourshift") = 0) .def("evaluate", &libdar::crit_in_place_data_more_recent::evaluate) .def("clone", &libdar::crit_in_place_data_more_recent::clone); pybind11::class_(mod, "crit_in_place_data_more_recent_or_equal_to") .def(pybind11::init(), pybind11::arg("date"), pybind11::arg("hourshift") = 0) .def("evaluate", &libdar::crit_in_place_data_more_recent_or_equal_to::evaluate) .def("clone", &libdar::crit_in_place_data_more_recent_or_equal_to::clone); pybind11::class_(mod, "crit_in_place_data_bigger") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_data_bigger::evaluate) .def("clone", &libdar::crit_in_place_data_bigger::clone); pybind11::class_(mod, "crit_in_place_data_saved") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_data_saved::evaluate) .def("clone", &libdar::crit_in_place_data_saved::clone); pybind11::class_(mod, "crit_in_place_data_dirty") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_data_dirty::evaluate) .def("clone", &libdar::crit_in_place_data_dirty::clone); pybind11::class_(mod, "crit_in_place_data_sparse") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_data_sparse::evaluate) .def("clone", &libdar::crit_in_place_data_sparse::clone); pybind11::class_(mod, "crit_in_place_has_delta_sig") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_has_delta_sig::evaluate) .def("clone", &libdar::crit_in_place_has_delta_sig::clone); pybind11::class_(mod, "crit_in_place_EA_present") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_EA_present::evaluate) .def("clone", &libdar::crit_in_place_EA_present::clone); pybind11::class_(mod, "crit_in_place_EA_more_recent") .def(pybind11::init(), pybind11::arg("hourshift") = 0) .def("evaluate", &libdar::crit_in_place_EA_more_recent::evaluate) .def("clone", &libdar::crit_in_place_EA_more_recent::clone); pybind11::class_(mod, "crit_in_place_EA_more_recent_or_equal_to") .def(pybind11::init(), pybind11::arg("date"), pybind11::arg("hourshift") = 0) .def("evaluate", &libdar::crit_in_place_EA_more_recent_or_equal_to::evaluate) .def("clone", &libdar::crit_in_place_EA_more_recent_or_equal_to::clone); pybind11::class_(mod, "crit_in_place_more_EA") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_more_EA::evaluate) .def("clone", &libdar::crit_in_place_more_EA::clone); pybind11::class_(mod, "crit_in_place_EA_bigger") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_EA_bigger::evaluate) .def("clone", &libdar::crit_in_place_EA_bigger::clone); pybind11::class_(mod, "crit_in_place_EA_saved") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_in_place_EA_saved::evaluate) .def("clone", &libdar::crit_in_place_EA_saved::clone); pybind11::class_(mod, "crit_same_type") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_same_type::evaluate) .def("clone", &libdar::crit_same_type::clone); pybind11::class_(mod, "crit_not") .def(pybind11::init()) .def("evaluate", &libdar::crit_not::evaluate) .def("clone", &libdar::crit_not::clone); pybind11::class_(mod, "crit_and") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_and::evaluate) .def("clone", &libdar::crit_and::clone) .def("add_crit", &libdar::crit_and::add_crit) .def("clear", &libdar::crit_and::clear) .def("gobe", &libdar::crit_and::gobe); pybind11::class_(mod, "crit_or") .def(pybind11::init<>()) .def("evaluate", &libdar::crit_or::evaluate) .def("clone", &libdar::crit_or::clone) .def("add_crit", &libdar::crit_or::add_crit) .def("clear", &libdar::crit_or::clear) .def("gobe", &libdar::crit_or::gobe); pybind11::class_(mod, "crit_invert") .def(pybind11::init()) .def("evaluate", &libdar::crit_invert::evaluate) .def("clone", &libdar::crit_invert::clone); /////////////////////////////////////////// // binding for what's in crit_action.hpp // pybind11::enum_(mod, "saved_status") .value("saved", libdar::saved_status::saved) .value("inode_only", libdar::saved_status::inode_only) .value("fake", libdar::saved_status::fake) .value("not_saved", libdar::saved_status::not_saved) .value("delta", libdar::saved_status::delta) .export_values(); pybind11::enum_(mod, "over_action_data") .value("data_preserve", libdar::data_preserve) .value("data_overwrite", libdar::data_overwrite) .value("data_preserve_mark_already_saved", libdar::data_preserve_mark_already_saved) .value("data_overwrite_mark_already_saved", libdar::data_overwrite_mark_already_saved) .value("data_remove", libdar::data_remove) .value("data_undefined", libdar::data_undefined) .value("data_ask", libdar::data_ask) .export_values(); pybind11::enum_(mod, "over_action_ea") .value("EA_preserve", libdar::EA_preserve) .value("EA_overwrite", libdar::EA_overwrite) .value("EA_clear", libdar::EA_clear) .value("EA_preserve_mark_already_saved", libdar::EA_preserve_mark_already_saved) .value("EA_overwrite_mark_already_saved", libdar::EA_overwrite_mark_already_saved) .value("EA_merge_preserve", libdar::EA_merge_preserve) .value("EA_merge_overwrite", libdar::EA_merge_overwrite) .value("EA_undefined", libdar::EA_undefined) .value("EA_ask", libdar::EA_ask) .export_values(); class pycrit_action : public libdar::crit_action { public: virtual void get_action(const libdar::cat_nomme & first, const libdar::cat_nomme & second, libdar::over_action_data & data, libdar::over_action_ea & ea) const override { PYBIND11_OVERRIDE_PURE(void, libdar::crit_action, get_action, first, second, data, ea); }; virtual libdar::crit_action *clone() const override { PYBIND11_OVERRIDE_PURE(libdar::crit_action *, libdar::crit_action, clone,); // training comma is expected as there is no argument to this method }; }; pybind11::class_(mod, "crit_action") .def(pybind11::init<>()) .def("get_action", &libdar::crit_action::get_action) .def("clone", &libdar::crit_action::clone); pybind11::class_(mod, "crit_constant_action") .def(pybind11::init()) .def("get_action", &libdar::crit_constant_action::get_action) .def("clone", &libdar::crit_constant_action::clone); pybind11::class_(mod, "testing") .def(pybind11::init()) .def("get_action", &libdar::testing::get_action) .def("clone", &libdar::testing::clone); pybind11::class_(mod, "crit_chain") .def(pybind11::init<>()) .def("add", &libdar::crit_chain::add) .def("clear", &libdar::crit_chain::clear) .def("gobe", &libdar::crit_chain::gobe) .def("get_action", &libdar::crit_chain::get_action) .def("clone", &libdar::crit_chain::clone); /////////////////////////////////////////// // secu_string classes // pybind11::class_(mod, "secu_string") .def_static("is_string_secured", &libdar::secu_string::is_string_secured) .def(pybind11::init(), pybind11::arg("storage_size") = 0) .def(pybind11::init()) .def(pybind11::self != pybind11::self) .def(pybind11::self == pybind11::self) .def("set", &libdar::secu_string::set) .def("append_at", (void (libdar::secu_string::*)(libdar::U_I, const char *, libdar::U_I))&libdar::secu_string::append_at) .def("append_at", (void (libdar::secu_string::*)(libdar::U_I, int, libdar::U_I))&libdar::secu_string::append_at) .def("append", (void (libdar::secu_string::*)(const char *, libdar::U_I)) &libdar::secu_string::append) .def("append", (void (libdar::secu_string::*)(int, libdar::U_I)) &libdar::secu_string::append) .def("reduce_string_size_to", &libdar::secu_string::reduce_string_size_to) .def("clear", &libdar::secu_string::clear) .def("resize", &libdar::secu_string::resize) .def("randomize", &libdar::secu_string::randomize) .def("c_str", (char* (libdar::secu_string::*)()) &libdar::secu_string::c_str, pybind11::return_value_policy::reference_internal) .def("c_str", (const char* (libdar::secu_string::*)() const) &libdar::secu_string::c_str, pybind11::return_value_policy::reference_internal) .def("get_array", &libdar::secu_string::get_array, pybind11::return_value_policy::reference_internal) .def("at", [](libdar::secu_string & self, libdar::U_I index) { return std::string(1, self[index]); }) // not a temporary secure storage but no char type in python... so .def("get_size", &libdar::secu_string::get_size) .def("empty", &libdar::secu_string::empty) .def("get_allocated_size", &libdar::secu_string::get_allocated_size); /////////////////////////////////////////// // archive_options_listing_shell classes // pybind11::class_ aols(mod, "archive_options_listing_shell"); pybind11::enum_(aols, "listformat") .value("normal", libdar::archive_options_listing_shell::normal) .value("tree", libdar::archive_options_listing_shell::tree) .value("xml", libdar::archive_options_listing_shell::xml) .value("slicing", libdar::archive_options_listing_shell::slicing); aols .def("clear", &libdar::archive_options_listing_shell::clear) .def("set_list_mode", &libdar::archive_options_listing_shell::set_list_mode) .def("set_sizes_in_bytes", &libdar::archive_options_listing_shell::set_sizes_in_bytes) .def("get_list_mode", &libdar::archive_options_listing_shell::get_list_mode) .def("get_sizes_in_bytes", &libdar::archive_options_listing_shell::get_sizes_in_bytes); /////////////////////////////////////////// // user_interaction classes // class py_user_interaction : public libdar::user_interaction { public: using libdar::user_interaction::user_interaction; protected: virtual void inherited_message(const std::string & message) override { PYBIND11_OVERRIDE_PURE( void, libdar::user_interaction, inherited_message, message); }; virtual bool inherited_pause(const std::string & message) override { PYBIND11_OVERRIDE_PURE( bool, libdar::user_interaction, inherited_pause, message); }; virtual std::string inherited_get_string(const std::string & message, bool echo) override { PYBIND11_OVERRIDE_PURE( std::string, libdar::user_interaction, inherited_get_string, message, echo); } virtual libdar::secu_string inherited_get_secu_string(const std::string & message, bool echo) override { PYBIND11_OVERRIDE_PURE( libdar::secu_string, libdar::user_interaction, inherited_get_secu_string, message, echo); } private: using libdar::user_interaction::printf; }; pybind11::class_, py_user_interaction>(mod, "user_interaction") .def(pybind11::init<>()); /////////////////////////////////////////// // entrepot_* classes // class py_entrepot : public libdar::entrepot { public: using libdar::entrepot::entrepot; virtual void set_location(const libdar::path & chemin) override { PYBIND11_OVERRIDE( void, libdar::entrepot, set_location, chemin); }; virtual void set_root(const libdar::path & p_root) override { PYBIND11_OVERRIDE( void, libdar::entrepot, set_root, p_root); }; virtual libdar::path get_full_path() const override { PYBIND11_OVERRIDE( libdar::path, libdar::entrepot, get_full_path, ); }; virtual std::string get_url() const override { PYBIND11_OVERRIDE_PURE( std::string, libdar::entrepot, get_url,); }; virtual const libdar::path & get_location() const override { PYBIND11_OVERRIDE( const libdar::path &, libdar::entrepot, get_location, ); }; virtual const libdar::path & get_root() const override { PYBIND11_OVERRIDE( const libdar::path &, libdar::entrepot, get_root, ); }; virtual void read_dir_reset() const override { PYBIND11_OVERRIDE_PURE( void, libdar::entrepot, read_dir_reset, ); }; virtual bool read_dir_next(std::string & filename) const override { PYBIND11_OVERRIDE_PURE( bool, libdar::entrepot, read_dir_next, filename); }; virtual entrepot *clone() const override { PYBIND11_OVERRIDE_PURE( entrepot *, libdar::entrepot, clone, ); }; protected: virtual libdar::fichier_global *inherited_open(const std::shared_ptr & dialog, const std::string & filename, libdar::gf_mode mode, bool force_permission, libdar::U_I permission, bool fail_if_exists, bool erase) const override { PYBIND11_OVERRIDE_PURE( libdar::fichier_global *, libdar::entrepot, inherited_open, dialog, filename, mode, force_permission, fail_if_exists, erase); }; virtual void inherited_unlink(const std::string & filename) const override { PYBIND11_OVERRIDE_PURE( void, libdar::entrepot, inherited_unlink, filename); }; virtual void read_dir_flush() override { PYBIND11_OVERRIDE_PURE( void, libdar::entrepot, read_dir_flush, ); }; }; pybind11::class_ >(mod, "entrepot") .def(pybind11::init<>()); pybind11::class_ >(mod, "entrepot_local") .def(pybind11::init()) .def("get_url", &libdar::entrepot_local::get_url) .def("read_dir_reset", &libdar::entrepot_local::read_dir_reset) .def("read_dir_next", &libdar::entrepot_local::read_dir_next) .def("clone", &libdar::entrepot_local::clone) .def("set_location", &libdar::entrepot_local::set_location) .def("set_root", &libdar::entrepot_local::set_root) .def("get_full_path", &libdar::entrepot_local::get_full_path) .def("get_location", &libdar::entrepot_local::get_location) .def("get_root", &libdar::entrepot_local::get_root); pybind11::enum_(mod, "mycurl_protocol") .value("proto_ftp", libdar::mycurl_protocol::proto_ftp) .value("proto_sftp", libdar::mycurl_protocol::proto_sftp); pybind11::class_ >(mod, "entrepot_libcurl") .def(pybind11::init< const std::shared_ptr &, libdar::mycurl_protocol, const std::string &, const libdar::secu_string &, const std::string &, const std::string &, bool, const std::string &, const std::string &, const std::string &, libdar::U_I>()) .def("get_url", &libdar::entrepot_libcurl::get_url) .def("read_dir_reset", &libdar::entrepot_libcurl::read_dir_reset) .def("read_dir_next", &libdar::entrepot_libcurl::read_dir_next) .def("clone", &libdar::entrepot_libcurl::clone) .def("set_location", &libdar::entrepot_libcurl::set_location) .def("set_root", &libdar::entrepot_libcurl::set_root) .def("get_full_path", &libdar::entrepot_libcurl::get_full_path) .def("get_location", &libdar::entrepot_libcurl::get_location) .def("get_root", &libdar::entrepot_libcurl::get_root); /////////////////////////////////////////// // fsa_* classes / enums // pybind11::enum_(mod, "fsa_family") .value("fsaf_hfs_plus", libdar::fsa_family::fsaf_hfs_plus) .value("fsaf_linux_extX", libdar::fsa_family::fsaf_linux_extX); pybind11::enum_(mod, "fsa_nature") .value("fsan_unset", libdar::fsa_nature::fsan_unset) .value("fsan_creation_date", libdar::fsa_nature::fsan_creation_date) .value("fsan_append_only", libdar::fsa_nature::fsan_append_only) .value("fsan_compressed", libdar::fsa_nature::fsan_compressed) .value("fsan_no_dump", libdar::fsa_nature::fsan_no_dump) .value("fsan_immutable", libdar::fsa_nature::fsan_immutable) .value("fsan_data_journaling", libdar::fsa_nature::fsan_data_journaling) .value("fsan_secure_deletion", libdar::fsa_nature::fsan_secure_deletion) .value("fsan_no_tail_merging", libdar::fsa_nature::fsan_no_tail_merging) .value("fsan_undeletable", libdar::fsa_nature::fsan_undeletable) .value("fsan_noatime_update", libdar::fsa_nature::fsan_noatime_update) .value("fsan_sychronous_directory", libdar::fsa_nature::fsan_synchronous_directory) .value("fsan_sychronous_update", libdar::fsa_nature::fsan_synchronous_update) .value("fsan_top_of_dir_hierachy", libdar::fsa_nature::fsan_top_of_dir_hierarchy); mod.def("fsa_family_to_string", &libdar::fsa_family_to_string); mod.def("fsa_nature_to_string", &libdar::fsa_nature_to_string); mod.def("all_fsa_families", &libdar::all_fsa_families); mod.def("fsa_scope_to_infinint", &libdar::fsa_scope_to_infinint); mod.def("infinint_to_fsa_scope", &libdar::infinint_to_fsa_scope); mod.def("fsa_scope_to_string", &libdar::fsa_scope_to_string); /////////////////////////////////////////// // compile_time routines // class compile_time { public: static bool ea() noexcept { return libdar::compile_time::ea(); }; static bool largefile() noexcept { return libdar::compile_time::largefile(); }; static bool nodump() noexcept { return libdar::compile_time::nodump(); }; static bool special_alloc() noexcept { return libdar::compile_time::special_alloc(); }; static libdar::U_I bits() noexcept { return libdar::compile_time::bits(); }; static bool thread_safe() noexcept { return libdar::compile_time::thread_safe(); }; static bool libz() noexcept { return libdar::compile_time::libz(); }; static bool libbz2() noexcept { return libdar::compile_time::libbz2(); }; static bool liblzo() noexcept { return libdar::compile_time::liblzo(); }; static bool libxz() noexcept { return libdar::compile_time::libxz(); }; static bool libzstd() noexcept { return libdar::compile_time::libzstd(); }; static bool libgcrypt() noexcept { return libdar::compile_time::libgcrypt(); }; static bool furtive_read() noexcept { return libdar::compile_time::furtive_read(); }; static bool system_endian() noexcept { return libdar::compile_time::system_endian(); }; static bool posix_fadvise() noexcept { return libdar::compile_time::posix_fadvise(); }; static bool fast_dir() noexcept { return libdar::compile_time::fast_dir(); }; static bool FSA_linux_extX() noexcept { return libdar::compile_time::FSA_linux_extX(); }; static bool FSA_birthtime() noexcept { return libdar::compile_time::FSA_birthtime(); }; static bool microsecond_read() noexcept { return libdar::compile_time::microsecond_read(); }; static bool nanosecond_read() noexcept { return libdar::compile_time::nanosecond_read(); }; static bool microsecond_write() noexcept { return libdar::compile_time::microsecond_write(); }; static bool nanosecond_write() noexcept { return libdar::compile_time::nanosecond_write(); }; static bool symlink_restore_dates() noexcept { return libdar::compile_time::symlink_restore_dates(); }; static bool public_key_cipher() noexcept { return libdar::compile_time::public_key_cipher(); }; static bool libthreadar() noexcept { return libdar::compile_time::libthreadar(); }; static std::string libthreadar_version() noexcept { return libdar::compile_time::libthreadar_version(); }; static bool librsync() noexcept { return libdar::compile_time::librsync(); }; static bool remote_repository() noexcept { return libdar::compile_time::remote_repository(); }; }; pybind11::class_ c_t(mod, "compile_time"); pybind11::enum_(c_t, "endian") .value("big", libdar::compile_time::big) .value("little", libdar::compile_time::little) .value("error", libdar::compile_time::error); c_t .def_static("ea", &compile_time::ea) .def_static("largefile", &compile_time::largefile) .def_static("nodump", &compile_time::nodump) .def_static("special_alloc", &compile_time::special_alloc) .def_static("bits", &compile_time::bits) .def_static("thread_safe", &compile_time::thread_safe) .def_static("libz", &compile_time::libz) .def_static("libbz2", &compile_time::libbz2) .def_static("liblzo", &compile_time::liblzo) .def_static("libxz", &compile_time::libxz) .def_static("libzstd", &compile_time::libzstd) .def_static("libgcrypt", &compile_time::libgcrypt) .def_static("furtive_read", &compile_time::furtive_read) .def_static("system_endian", &compile_time::system_endian) .def_static("posix_fadvise", &compile_time::posix_fadvise) .def_static("fast_dir", &compile_time::fast_dir) .def_static("FSA_linux_extX", &compile_time::FSA_linux_extX) .def_static("FSA_birthtime", &compile_time::FSA_birthtime) .def_static("microsecond_read", &compile_time::microsecond_read) .def_static("microsecond_write", &compile_time::microsecond_write) .def_static("symlink_restore_dates", &compile_time::symlink_restore_dates) .def_static("public_key_cipher", &compile_time::public_key_cipher) .def_static("libthreadar", &compile_time::libthreadar) .def_static("libthreadar_version", &compile_time::libthreadar_version) .def_static("librsync", &compile_time::librsync) .def_static("remote_repository", &compile_time::remote_repository); /////////////////////////////////////////// // archive_aux_* structures and routines // pybind11::enum_(mod, "modified_data_detection") .value("any_inode_change", libdar::modified_data_detection::any_inode_change) .value("mtime_size", libdar::modified_data_detection::mtime_size); pybind11::enum_(mod, "comparison_fields") .value("all", libdar::comparison_fields::all) .value("ignore_owner", libdar::comparison_fields::ignore_owner) .value("mtime", libdar::comparison_fields::mtime) .value("inode_type", libdar::comparison_fields::inode_type); pybind11::enum_(mod, "hash_algo") .value("none", libdar::hash_algo::none) .value("md5", libdar::hash_algo::md5) .value("sha1", libdar::hash_algo::sha1) .value("sha512", libdar::hash_algo::sha512) .value("argon2", libdar::hash_algo::argon2); mod.def("hash_algo_to_string", &libdar::hash_algo_to_string); mod.def("string_to_hash_algo", [](const std::string & arg) { libdar::hash_algo val = libdar::hash_algo::none; bool ret = libdar::string_to_hash_algo(arg, val); return std::make_tuple(ret, val); }); /////////////////////////////////////////// // compression_* structures and routines // pybind11::enum_(mod, "compression") .value("none", libdar::compression::none) .value("gzip", libdar::compression::gzip) .value("bzip2", libdar::compression::bzip2) .value("lzo", libdar::compression::lzo) .value("xz", libdar::compression::xz) .value("lzo1x_1_15", libdar::compression::lzo1x_1_15) .value("lzo1x_1", libdar::compression::lzo1x_1) .value("zstd", libdar::compression::zstd) .value("lz4", libdar::compression::lz4); mod.def("compression2string", &libdar::compression2string); mod.def("string2compression", &libdar::string2compression); /////////////////////////////////////////// // delta_sig_block_size datastructure // pybind11::class_ dsbs(mod, "delta_sig_block_size"); pybind11::enum_(dsbs, "fs_function_t") .value("fixed", libdar::delta_sig_block_size::fixed) .value("linear", libdar::delta_sig_block_size::linear) .value("log2", libdar::delta_sig_block_size::log2) .value("square2", libdar::delta_sig_block_size::square2) .value("square3", libdar::delta_sig_block_size::square3); dsbs .def_readwrite("fs_function", &libdar::delta_sig_block_size::fs_function) .def_readwrite("multiplier", &libdar::delta_sig_block_size::multiplier) .def_readwrite("divisor", &libdar::delta_sig_block_size::divisor) .def_readwrite("min_block_len", &libdar::delta_sig_block_size::min_block_len) .def_readwrite("max_block_len", &libdar::delta_sig_block_size::max_block_len) .def(pybind11::self == pybind11::self) .def("reset", &libdar::delta_sig_block_size::reset) .def("equals_defaults", &libdar::delta_sig_block_size::equals_default) .def("check", &libdar::delta_sig_block_size::check) .def("calculate", &libdar::delta_sig_block_size::calculate); /////////////////////////////////////////// // archive_options_* classes // pybind11::class_(mod, "archive_options_read") .def(pybind11::init<>()) .def("clear", &libdar::archive_options_read::clear) .def("set_crypto_algo", &libdar::archive_options_read::set_crypto_algo) .def("set_crypto_pass", &libdar::archive_options_read::set_crypto_pass) .def("set_crypto_size", &libdar::archive_options_read::set_crypto_size) .def("set_default_crypto_size", &libdar::archive_options_read::set_default_crypto_size) .def("set_input_pipe", &libdar::archive_options_read::set_input_pipe) .def("set_output_pipe", &libdar::archive_options_read::set_output_pipe) .def("set_execute", &libdar::archive_options_read::set_execute) .def("set_info_details", &libdar::archive_options_read::set_info_details) .def("set_lax", &libdar::archive_options_read::set_lax) .def("set_sequential_read", &libdar::archive_options_read::set_sequential_read) .def("set_slice_min_digits", &libdar::archive_options_read::set_slice_min_digits) .def("set_entrepot", &libdar::archive_options_read::set_entrepot) .def("set_ignore_signature_check_failure", &libdar::archive_options_read::set_ignore_signature_check_failure) .def("set_multi_threaded", &libdar::archive_options_read::set_multi_threaded) .def("set_multi_threaded_crypto", &libdar::archive_options_read::set_multi_threaded_crypto) .def("set_multi_threaded_compress", &libdar::archive_options_read::set_multi_threaded_compress) .def("set_external_catalogue", &libdar::archive_options_read::set_external_catalogue) .def("unset_external_catalogue", &libdar::archive_options_read::unset_external_catalogue) .def("set_ref_crypto_algo", &libdar::archive_options_read::set_ref_crypto_algo) .def("set_ref_crypto_pass", &libdar::archive_options_read::set_ref_crypto_pass) .def("set_ref_crypto_size", &libdar::archive_options_read::set_ref_crypto_size) .def("set_ref_execute", &libdar::archive_options_read::set_ref_execute) .def("set_ref_slice_min_digits", &libdar::archive_options_read::set_ref_slice_min_digits) .def("set_ref_entrepot", &libdar::archive_options_read::set_ref_entrepot) .def("set_header_only", &libdar::archive_options_read::set_header_only); pybind11::class_(mod, "archive_options_create") .def(pybind11::init<>()) .def("clear", &libdar::archive_options_create::clear) .def("set_reference", &libdar::archive_options_create::set_reference) .def("set_selection", &libdar::archive_options_create::set_selection) .def("set_subtree", &libdar::archive_options_create::set_subtree) .def("set_allow_over", &libdar::archive_options_create::set_allow_over) .def("set_warn_over", &libdar::archive_options_create::set_warn_over) .def("set_info_details", &libdar::archive_options_create::set_info_details) .def("set_display_treated", &libdar::archive_options_create::set_display_treated) .def("set_display_skipped", &libdar::archive_options_create::set_display_skipped) .def("set_display_finished", &libdar::archive_options_create::set_display_finished) .def("set_pause", &libdar::archive_options_create::set_pause) .def("set_empty_dir", &libdar::archive_options_create::set_empty_dir) .def("set_compression", &libdar::archive_options_create::set_compression) .def("set_compression_level", &libdar::archive_options_create::set_compression_level) .def("set_compression_block_size", &libdar::archive_options_create::set_compression_block_size) .def("set_slicing", &libdar::archive_options_create::set_slicing) .def("set_ea_mask", &libdar::archive_options_create::set_ea_mask) .def("set_execute", &libdar::archive_options_create::set_execute) .def("set_crypto_algo", &libdar::archive_options_create::set_crypto_algo) .def("set_crypto_pass", &libdar::archive_options_create::set_crypto_pass) .def("set_crypto_size", &libdar::archive_options_create::set_crypto_size) .def("set_gnupg_recipients", &libdar::archive_options_create::set_gnupg_recipients) .def("set_gnupg_signatories", &libdar::archive_options_create::set_gnupg_signatories) .def("set_compr_mask", &libdar::archive_options_create::set_compr_mask) .def("set_min_compr_size", &libdar::archive_options_create::set_min_compr_size) .def("set_nodump", &libdar::archive_options_create::set_nodump) .def("set_exclude_by_ea", &libdar::archive_options_create::set_exclude_by_ea) .def("set_what_to_check", &libdar::archive_options_create::set_what_to_check) .def("set_hourshift", &libdar::archive_options_create::set_hourshift) .def("set_empty", &libdar::archive_options_create::set_empty) .def("set_alter_atime", &libdar::archive_options_create::set_alter_atime) .def("set_furtive_read_mode", &libdar::archive_options_create::set_furtive_read_mode) .def("set_same_fs", &libdar::archive_options_create::set_same_fs) .def("set_snapshot", &libdar::archive_options_create::set_snapshot) .def("set_cache_directory_tagging", &libdar::archive_options_create::set_cache_directory_tagging) .def("set_fixed_date", &libdar::archive_options_create::set_fixed_date) .def("set_slice_permission", &libdar::archive_options_create::set_slice_permission) .def("set_slice_user_ownership", &libdar::archive_options_create::set_slice_user_ownership) .def("set_slice_group_ownership", &libdar::archive_options_create::set_slice_group_ownership) .def("set_retry_on_change", &libdar::archive_options_create::set_retry_on_change) .def("set_sequential_marks", &libdar::archive_options_create::set_sequential_marks) .def("set_sparse_file_min_size", &libdar::archive_options_create::set_sparse_file_min_size) .def("set_security_check", &libdar::archive_options_create::set_security_check) .def("set_user_comment", &libdar::archive_options_create::set_user_comment) .def("set_hash_algo", &libdar::archive_options_create::set_hash_algo) .def("set_slice_min_digits", &libdar::archive_options_create::set_slice_min_digits) .def("set_backup_hook", &libdar::archive_options_create::set_backup_hook) .def("set_ignore_unknow_inode_type", &libdar::archive_options_create::set_ignore_unknown_inode_type) .def("set_entrepot", &libdar::archive_options_create::set_entrepot) .def("set_fsa_scope", &libdar::archive_options_create::set_fsa_scope) .def("set_multi_threaded", &libdar::archive_options_create::set_multi_threaded) .def("set_multi_threaded_crypto", &libdar::archive_options_create::set_multi_threaded_crypto) .def("set_multi_threaded_compress", &libdar::archive_options_create::set_multi_threaded_compress) .def("set_delta_diff", &libdar::archive_options_create::set_delta_diff) .def("set_delta_signature", &libdar::archive_options_create::set_delta_signature) .def("set_delta_mask", &libdar::archive_options_create::set_delta_mask) .def("set_delta_sig_min_size", &libdar::archive_options_create::set_delta_sig_min_size) .def("set_auto_zeroing_neg_dates", &libdar::archive_options_create::set_auto_zeroing_neg_dates) .def("set_ignored_as_symlink", &libdar::archive_options_create::set_ignored_as_symlink) .def("set_modified_data_detection", &libdar::archive_options_create::set_modified_data_detection) .def("set_iteration_count", &libdar::archive_options_create::set_iteration_count) .def("set_kdf_hash", &libdar::archive_options_create::set_kdf_hash) .def("set_sig_block_len", &libdar::archive_options_create::set_sig_block_len); pybind11::class_(mod, "archive_options_isolate") .def(pybind11::init<>()) .def("clear", &libdar::archive_options_isolate::clear) .def("set_allow_over", &libdar::archive_options_isolate::set_allow_over) .def("set_warn_over", &libdar::archive_options_isolate::set_warn_over) .def("set_info_details", &libdar::archive_options_isolate::set_info_details) .def("set_pause", &libdar::archive_options_isolate::set_pause) .def("set_compression", &libdar::archive_options_isolate::set_compression) .def("set_compression_level", &libdar::archive_options_isolate::set_compression_level) .def("set_compression_block_size", &libdar::archive_options_isolate::set_compression_block_size) .def("set_slicing", &libdar::archive_options_isolate::set_slicing) .def("set_execute", &libdar::archive_options_isolate::set_execute) .def("set_crypto_algo", &libdar::archive_options_isolate::set_crypto_algo) .def("set_crypto_pass", &libdar::archive_options_isolate::set_crypto_pass) .def("set_crypto_size", &libdar::archive_options_isolate::set_crypto_size) .def("set_gnupg_recipients", &libdar::archive_options_isolate::set_gnupg_recipients) .def("set_gnupg_signatories", &libdar::archive_options_isolate::set_gnupg_signatories) .def("set_empty", &libdar::archive_options_isolate::set_empty) .def("set_slice_permission", &libdar::archive_options_isolate::set_slice_permission) .def("set_slice_user_ownership", &libdar::archive_options_isolate::set_slice_user_ownership) .def("set_slice_group_ownership", &libdar::archive_options_isolate::set_slice_group_ownership) .def("set_user_comment", &libdar::archive_options_isolate::set_user_comment) .def("set_hash_algo", &libdar::archive_options_isolate::set_hash_algo) .def("set_slice_min_digits", &libdar::archive_options_isolate::set_slice_min_digits) .def("set_sequential_marks", &libdar::archive_options_isolate::set_sequential_marks) .def("set_entrepot", &libdar::archive_options_isolate::set_entrepot) .def("set_multi_threaded", &libdar::archive_options_isolate::set_multi_threaded) .def("set_multi_threaded_crypto", &libdar::archive_options_isolate::set_multi_threaded_crypto) .def("set_multi_threaded_compress", &libdar::archive_options_isolate::set_multi_threaded_compress) .def("set_delta_signature", &libdar::archive_options_isolate::set_delta_signature) .def("set_delta_mask", &libdar::archive_options_isolate::set_delta_mask) .def("set_delta_sig_min_size", &libdar::archive_options_isolate::set_delta_sig_min_size) .def("set_iteration_count", &libdar::archive_options_isolate::set_iteration_count) .def("set_kdf_hash", &libdar::archive_options_isolate::set_kdf_hash) .def("set_sig_block_len", &libdar::archive_options_isolate::set_sig_block_len); pybind11::class_(mod, "archive_options_merge") .def(pybind11::init<>()) .def("clear", &libdar::archive_options_merge::clear) .def("set_auxiliary_ref", &libdar::archive_options_merge::set_auxiliary_ref) .def("set_selection", &libdar::archive_options_merge::set_selection) .def("set_subtree", &libdar::archive_options_merge::set_subtree) .def("set_allow_over", &libdar::archive_options_merge::set_allow_over) .def("set_warn_over", &libdar::archive_options_merge::set_warn_over) .def("set_overwriting_rules", &libdar::archive_options_merge::set_overwriting_rules) .def("set_info_details", &libdar::archive_options_merge::set_info_details) .def("set_display_treated", &libdar::archive_options_merge::set_display_treated) .def("set_display_skipped", &libdar::archive_options_merge::set_display_skipped) .def("set_pause", &libdar::archive_options_merge::set_pause) .def("set_empty_dir", &libdar::archive_options_merge::set_empty_dir) .def("set_compression", &libdar::archive_options_merge::set_compression) .def("set_compression_level", &libdar::archive_options_merge::set_compression_level) .def("set_compression_block_size", &libdar::archive_options_merge::set_compression_block_size) .def("set_slicing", &libdar::archive_options_merge::set_slicing) .def("set_ea_mask", &libdar::archive_options_merge::set_ea_mask) .def("set_execute", &libdar::archive_options_merge::set_execute) .def("set_crypto_algo", &libdar::archive_options_merge::set_crypto_algo) .def("set_crypto_pass", &libdar::archive_options_merge::set_crypto_pass) .def("set_crypto_size", &libdar::archive_options_merge::set_crypto_size) .def("set_gnupg_recipients", &libdar::archive_options_merge::set_gnupg_recipients) .def("set_gnupg_signatories", &libdar::archive_options_merge::set_gnupg_signatories) .def("set_compr_mask", &libdar::archive_options_merge::set_compr_mask) .def("set_min_compr_size", &libdar::archive_options_merge::set_min_compr_size) .def("set_empty", &libdar::archive_options_merge::set_empty) .def("set_keep_compressed", &libdar::archive_options_merge::set_keep_compressed) .def("set_slice_permission", &libdar::archive_options_merge::set_slice_permission) .def("set_slice_user_ownership", &libdar::archive_options_merge::set_slice_user_ownership) .def("set_slice_group_ownership", &libdar::archive_options_merge::set_slice_group_ownership) .def("set_decremental_mode", &libdar::archive_options_merge::set_decremental_mode) .def("set_sequential_marks", &libdar::archive_options_merge::set_sequential_marks) .def("set_sparse_file_min_size", &libdar::archive_options_merge::set_sparse_file_min_size) .def("set_user_comment", &libdar::archive_options_merge::set_user_comment) .def("set_hash_algo", &libdar::archive_options_merge::set_hash_algo) .def("set_slice_min_digits", &libdar::archive_options_merge::set_slice_min_digits) .def("set_entrepot", &libdar::archive_options_merge::set_entrepot) .def("set_fsa_scope", &libdar::archive_options_merge::set_fsa_scope) .def("set_mutli_threaded", &libdar::archive_options_merge::set_multi_threaded) .def("set_mutli_threaded_crypto", &libdar::archive_options_merge::set_multi_threaded_crypto) .def("set_multi_threaded_compress", &libdar::archive_options_merge::set_multi_threaded_compress) .def("set_delta_signature", &libdar::archive_options_merge::set_delta_signature) .def("set_delta_mask", &libdar::archive_options_merge::set_delta_mask) .def("set_delta_sig_min_size", &libdar::archive_options_merge::set_delta_sig_min_size) .def("set_iteration_count", &libdar::archive_options_merge::set_iteration_count) .def("set_kdf_hash", &libdar::archive_options_merge::set_kdf_hash) .def("set_sig_block_len", &libdar::archive_options_merge::set_sig_block_len); pybind11::class_ py_archive_options_extract(mod, "archive_options_extract"); pybind11::enum_(py_archive_options_extract, "t_dirty") .value("dirty_ignore",libdar::archive_options_extract::t_dirty::dirty_ignore) .value("dirty_warn",libdar::archive_options_extract::t_dirty::dirty_warn) .value("dirty_ok", libdar::archive_options_extract::t_dirty::dirty_ok); py_archive_options_extract .def(pybind11::init<>()) .def("clear", &libdar::archive_options_extract::clear) .def("set_selection", &libdar::archive_options_extract::set_selection) .def("set_subtree", &libdar::archive_options_extract::set_subtree) .def("set_warn_over", &libdar::archive_options_extract::set_warn_over) .def("set_info_details", &libdar::archive_options_extract::set_info_details) .def("set_display_treated", &libdar::archive_options_extract::set_display_treated) .def("set_display_skipped", &libdar::archive_options_extract::set_display_skipped) .def("set_ea_mask", &libdar::archive_options_extract::set_ea_mask) .def("set_flat", &libdar::archive_options_extract::set_flat) .def("set_what_to_check", &libdar::archive_options_extract::set_what_to_check) .def("set_warn_remove_no_match", &libdar::archive_options_extract::set_warn_remove_no_match) .def("set_empty", &libdar::archive_options_extract::set_empty) .def("set_empty_dir", &libdar::archive_options_extract::set_empty_dir) .def("set_dirty_behavior", (void (libdar::archive_options_extract::*)(bool, bool)) &libdar::archive_options_extract::set_dirty_behavior) .def("set_dirty_behavior", (void (libdar::archive_options_extract::*)(libdar::archive_options_extract::t_dirty)) &libdar::archive_options_extract::set_dirty_behavior) .def("set_overwriting_rules", &libdar::archive_options_extract::set_overwriting_rules) .def("set_only_deleted", &libdar::archive_options_extract::set_only_deleted) .def("set_ignore_deleted", &libdar::archive_options_extract::set_ignore_deleted) .def("set_fsa_scope", &libdar::archive_options_extract::set_fsa_scope); pybind11::class_(mod, "archive_options_listing") .def(pybind11::init<>()) .def("clear", &libdar::archive_options_listing::clear) .def("set_info_details", &libdar::archive_options_listing::set_info_details) .def("set_selection", &libdar::archive_options_listing::set_selection) .def("set_subtree", &libdar::archive_options_listing::set_subtree) .def("set_filter_unsaved", &libdar::archive_options_listing::set_filter_unsaved) .def("set_slicing_location", &libdar::archive_options_listing::set_slicing_location) .def("set_user_slicing", &libdar::archive_options_listing::set_user_slicing) .def("set_display_ea", &libdar::archive_options_listing::set_display_ea); pybind11::class_(mod, "archive_options_diff") .def(pybind11::init<>()) .def("clear", &libdar::archive_options_diff::clear) .def("set_selection", &libdar::archive_options_diff::set_selection) .def("set_subtree", &libdar::archive_options_diff::set_subtree) .def("set_info_details", &libdar::archive_options_diff::set_info_details) .def("set_display_treated", &libdar::archive_options_diff::set_display_treated) .def("set_display_skipped", &libdar::archive_options_diff::set_display_skipped) .def("set_ea_mask", &libdar::archive_options_diff::set_ea_mask) .def("set_what_to_check", &libdar::archive_options_diff::set_what_to_check) .def("set_alter_atime", &libdar::archive_options_diff::set_alter_atime) .def("set_furtive_read_mode", &libdar::archive_options_diff::set_furtive_read_mode) .def("set_hourshift", &libdar::archive_options_diff::set_hourshift) .def("set_compare_symlink_date", &libdar::archive_options_diff::set_compare_symlink_date) .def("set_fsa_scope", &libdar::archive_options_diff::set_fsa_scope); pybind11::class_(mod, "archive_options_test") .def(pybind11::init<>()) .def("clear", &libdar::archive_options_test::clear) .def("set_selection", &libdar::archive_options_test::set_selection) .def("set_subtree", &libdar::archive_options_test::set_subtree) .def("set_info_details", &libdar::archive_options_test::set_info_details) .def("set_display_skipped", &libdar::archive_options_test::set_display_skipped) .def("set_display_treated", &libdar::archive_options_test::set_display_treated) .def("set_empty", &libdar::archive_options_test::set_empty); pybind11::class_(mod, "archive_options_repair") .def(pybind11::init<>()) .def("clear", &libdar::archive_options_repair::clear) .def("set_allow_over", &libdar::archive_options_repair::set_allow_over) .def("set_warn_over", &libdar::archive_options_repair::set_warn_over) .def("set_info_details", &libdar::archive_options_repair::set_info_details) .def("set_display_treated", &libdar::archive_options_repair::set_display_treated) .def("set_display_skipped", &libdar::archive_options_repair::set_display_skipped) .def("set_display_finished", &libdar::archive_options_repair::set_display_finished) .def("set_pause", &libdar::archive_options_repair::set_pause) .def("set_slicing", &libdar::archive_options_repair::set_slicing) .def("set_execute", &libdar::archive_options_repair::set_execute) .def("set_crypto_algo", &libdar::archive_options_repair::set_crypto_algo) .def("set_crypto_pass", &libdar::archive_options_repair::set_crypto_pass) .def("set_crypto_size", &libdar::archive_options_repair::set_crypto_size) .def("set_gnupg_recipients", &libdar::archive_options_repair::set_gnupg_recipients) .def("set_gnupg_signatories", &libdar::archive_options_repair::set_gnupg_signatories) .def("set_empty", &libdar::archive_options_repair::set_empty) .def("set_slice_permission", &libdar::archive_options_repair::set_slice_permission) .def("set_slice_user_ownership", &libdar::archive_options_repair::set_slice_user_ownership) .def("set_slice_group_ownership", &libdar::archive_options_repair::set_slice_group_ownership) .def("set_user_comment", &libdar::archive_options_repair::set_user_comment) .def("set_hash_algo", &libdar::archive_options_repair::set_hash_algo) .def("set_slice_min_digits", &libdar::archive_options_repair::set_slice_min_digits) .def("set_entrepot", &libdar::archive_options_repair::set_entrepot) .def("set_multi_threaded", &libdar::archive_options_repair::set_multi_threaded) .def("set_multi_threaded_crypto", &libdar::archive_options_repair::set_multi_threaded_crypto) .def("set_multi_threaded_compress", &libdar::archive_options_repair::set_multi_threaded_compress); ////////////////////////////////////////// // entree_stats classes // pybind11::class_(mod, "entree_stats") .def_readonly("num_x", &libdar::entree_stats::num_x) .def_readonly("num_d", &libdar::entree_stats::num_d) .def_readonly("num_f", &libdar::entree_stats::num_f) .def_readonly("num_c", &libdar::entree_stats::num_c) .def_readonly("num_b", &libdar::entree_stats::num_b) .def_readonly("num_p", &libdar::entree_stats::num_p) .def_readonly("num_s", &libdar::entree_stats::num_s) .def_readonly("num_l", &libdar::entree_stats::num_l) .def_readonly("num_D", &libdar::entree_stats::num_D) .def_readonly("num_hard_linked_inodes", &libdar::entree_stats::num_hard_linked_inodes) .def_readonly("num_hard_link_entries", &libdar::entree_stats::num_hard_link_entries) .def_readonly("saved", &libdar::entree_stats::saved) .def_readonly("patched", &libdar::entree_stats::patched) .def_readonly("inode_only", &libdar::entree_stats::inode_only) .def_readonly("total", &libdar::entree_stats::total) .def("clear", &libdar::entree_stats::clear) .def("listing", &libdar::entree_stats::listing); /////////////////////////////////////////// // archive_summary classes // pybind11::class_(mod, "archive_summary") .def("get_slice_size", &libdar::archive_summary::get_slice_size, pybind11::return_value_policy::reference_internal) .def("get_first_slice_size", &libdar::archive_summary::get_first_slice_size, pybind11::return_value_policy::reference_internal) .def("get_last_slice_size", &libdar::archive_summary::get_last_slice_size, pybind11::return_value_policy::reference_internal) .def("get_slice_number", &libdar::archive_summary::get_slice_number,pybind11::return_value_policy::reference_internal) .def("get_archive_size", &libdar::archive_summary::get_archive_size, pybind11::return_value_policy::reference_internal) .def("get_catalog_size", &libdar::archive_summary::get_catalog_size, pybind11::return_value_policy::reference_internal) .def("get_storage_size", &libdar::archive_summary::get_storage_size, pybind11::return_value_policy::reference_internal) .def("get_data_size", &libdar::archive_summary::get_data_size, pybind11::return_value_policy::reference_internal) .def("get_contents", &libdar::archive_summary::get_contents, pybind11::return_value_policy::reference_internal) .def("get_edition", &libdar::archive_summary::get_edition) .def("get_compression_algo", &libdar::archive_summary::get_compression_algo) .def("get_user_comment", &libdar::archive_summary::get_user_comment) .def("get_cipher", &libdar::archive_summary::get_cipher) .def("get_asym", &libdar::archive_summary::get_asym) .def("get_signed", &libdar::archive_summary::get_signed) .def("get_tape_marks", &libdar::archive_summary::get_tape_marks) .def("clear", &libdar::archive_summary::clear); /////////////////////////////////////////// // list_entry classes // pybind11::class_(mod, "list_entry") .def("is_eod", &libdar::list_entry::is_eod) .def("get_name", &libdar::list_entry::get_name) .def("get_type", &libdar::list_entry::get_type) .def("is_dir", &libdar::list_entry::is_dir) .def("is_file", &libdar::list_entry::is_file) .def("is_symlink", &libdar::list_entry::is_symlink) .def("is_char_device", &libdar::list_entry::is_char_device) .def("is_block_device", &libdar::list_entry::is_block_device) .def("is_unix_socket", &libdar::list_entry::is_unix_socket) .def("is_named_pipe", &libdar::list_entry::is_named_pipe) .def("is_hard_linked", &libdar::list_entry::is_hard_linked) .def("is_removed_entry", &libdar::list_entry::is_removed_entry) .def("is_door_inode", &libdar::list_entry::is_door_inode) .def("is_empty_dir", &libdar::list_entry::is_empty_dir) .def("get_removed_type", &libdar::list_entry::get_removed_type) .def("has_data_present_in_the_archive", &libdar::list_entry::has_data_present_in_the_archive) .def("get_data_flag", &libdar::list_entry::get_data_flag) .def("get_data_status", &libdar::list_entry::get_data_status) .def("has_EA", &libdar::list_entry::has_EA) .def("has_EA_saved_in_the_archive", &libdar::list_entry::has_EA_saved_in_the_archive) .def("get_ea_flag", &libdar::list_entry::get_ea_flag) .def("get_ea_status", &libdar::list_entry::get_ea_status) .def("has_FSA", &libdar::list_entry::has_FSA) .def("has_FSA_saved_in_the_archive", &libdar::list_entry::has_FSA_saved_in_the_archive) .def("get_fsa_flag", &libdar::list_entry::get_fsa_flag) .def("get_uid", &libdar::list_entry::get_uid, pybind11::arg("try_resoling_name") = false) .def("get_gid", &libdar::list_entry::get_gid, pybind11::arg("try_resoling_name") = false) .def("get_perm", &libdar::list_entry::get_perm) .def("get_last_access", (std::string (libdar::list_entry::*)() const) &libdar::list_entry::get_last_access) .def("get_last_modif", (std::string (libdar::list_entry::*)() const) &libdar::list_entry::get_last_modif) .def("get_last_change", (std::string (libdar::list_entry::*)() const) &libdar::list_entry::get_last_change) .def("get_removal_date", &libdar::list_entry::get_removal_date) .def("get_last_access_s", &libdar::list_entry::get_last_access_s) .def("get_last_modif_s", &libdar::list_entry::get_last_modif_s) .def("get_last_change_s", &libdar::list_entry::get_last_change_s) .def("get_removal_date_s", &libdar::list_entry::get_removal_date_s) .def("get_file_size", &libdar::list_entry::get_file_size) .def("get_compression_ratio", &libdar::list_entry::get_compression_ratio) .def("get_compression_ratio_flag", &libdar::list_entry::get_compression_ratio_flag) .def("is_sparse", &libdar::list_entry::is_sparse) .def("get_sparse_flag", &libdar::list_entry::get_sparse_flag) .def("get_compression_algo", &libdar::list_entry::get_compression_algo) .def("is_dirty", &libdar::list_entry::is_dirty) .def("get_link_target", &libdar::list_entry::get_link_target) .def("get_major", &libdar::list_entry::get_major) .def("get_minor", &libdar::list_entry::get_minor) .def("has_delta_signature", &libdar::list_entry::has_delta_signature) .def("get_delta_flag", &libdar::list_entry::get_delta_flag) .def("get_archive_offset_for_data", (std::string (libdar::list_entry::*)() const) &libdar::list_entry::get_archive_offset_for_data) .def("get_archive_offset_for_EA", (std::string (libdar::list_entry::*)() const) &libdar::list_entry::get_archive_offset_for_EA) .def("get_archive_offset_for_FSA", (std::string (libdar::list_entry::*)() const) &libdar::list_entry::get_archive_offset_for_FSA) .def("get_storage_size_for_FSA", (std::string (libdar::list_entry::*)() const) &libdar::list_entry::get_storage_size_for_FSA) .def("get_ea_reaset_read", &libdar::list_entry::get_ea_reset_read) .def("get_ea_read_next", &libdar::list_entry::get_ea_read_next) .def("get_etiquette", &libdar::list_entry::get_etiquette) .def("get_fsa_scope", &libdar::list_entry::get_fsa_scope) .def("get_data_crc", &libdar::list_entry::get_data_crc) .def("get_delta_patch_base_crc", &libdar::list_entry::get_delta_patch_base_crc) .def("get_delta_patch_result_crc", &libdar::list_entry::get_delta_patch_result_crc) .def("clear", &libdar::list_entry::clear); /////////////////////////////////////////// // archive classes // class py_archive : public libdar::archive { public: py_archive(const std::shared_ptr & dialog, const libdar::path & chem, const std::string & basename, const std::string & extension, const libdar::archive_options_read & options): libdar::archive(dialog, chem, basename, extension, options) {}; // splitting the create constructor in two flavors // first with progressive report by mean of shared_ptr py_archive(const std::shared_ptr & dialog, const libdar::path & fs_root, const libdar::path & sauv_path, const std::string & filename, const std::string & extension, const libdar::archive_options_create & options, std::shared_ptr progressive_report): libdar::archive(dialog, fs_root, sauv_path, filename, extension, options, progressive_report.get()) {}; // second without progressive report py_archive(const std::shared_ptr & dialog, const libdar::path & fs_root, const libdar::path & sauv_path, const std::string & filename, const std::string & extension, const libdar::archive_options_create & options): libdar::archive(dialog, fs_root, sauv_path, filename, extension, options, nullptr) {}; // splitting in two the merge constructor // first with progressive report by mean of a shared_ptr py_archive(const std::shared_ptr & dialog, const libdar::path & sauv_path, std::shared_ptr ref_arch1, const std::string & filename, const std::string & extension, const libdar::archive_options_merge & options, std::shared_ptr progressive_report): libdar::archive(dialog, sauv_path, ref_arch1, filename, extension, options, progressive_report.get()) {}; // second without progressive report py_archive(const std::shared_ptr & dialog, const libdar::path & sauv_path, std::shared_ptr ref_arch1, const std::string & filename, const std::string & extension, const libdar::archive_options_merge & options): libdar::archive(dialog, sauv_path, ref_arch1, filename, extension, options, nullptr) {}; py_archive(const std::shared_ptr & dialog, const libdar::path & chem_src, const std::string & basename_src, const std::string & extension_src, const libdar::archive_options_read & options_read, const libdar::path & chem_dst, const std::string & basename_dst, const std::string & extension_dst, const libdar::archive_options_repair & options_repair): libdar::archive(dialog, chem_src, basename_src, extension_src, options_read, chem_dst, basename_dst, extension_dst, options_repair) {}; libdar::statistics op_extract(const libdar::path & fs_root, const libdar::archive_options_extract & options, std::shared_ptr progressive_report) { return libdar::archive::op_extract(fs_root, options, progressive_report.get()); }; libdar::statistics op_extract(const libdar::path & fs_root, const libdar::archive_options_extract & options) { return libdar::archive::op_extract(fs_root, options, nullptr); }; libdar::statistics op_diff(const libdar::path & fs_root, const libdar::archive_options_diff & options) { return libdar::archive::op_diff(fs_root, options, nullptr); }; libdar::statistics op_diff(const libdar::path & fs_root, const libdar::archive_options_diff & options, std::shared_ptr progressive_report) { return libdar::archive::op_diff(fs_root, options, progressive_report.get()); }; libdar::statistics op_test(const libdar::archive_options_test & options) { return libdar::archive::op_test(options, nullptr); }; libdar::statistics op_test(const libdar::archive_options_test & options, std::shared_ptr progressive_report) { return libdar::archive::op_test(options, progressive_report.get()); }; }; pybind11::class_ >(mod, "archive") .def(pybind11::init< const std::shared_ptr &, const libdar::path, const std::string &, const std::string &, const libdar::archive_options_read & >()) .def(pybind11::init< const std::shared_ptr &, const libdar::path &, const libdar::path &, const std::string &, const std::string &, const libdar::archive_options_create &, std::shared_ptr >()) .def(pybind11::init< const std::shared_ptr &, const libdar::path &, const libdar::path &, const std::string &, const std::string &, const libdar::archive_options_create & >()) .def(pybind11::init< const std::shared_ptr &, const libdar::path &, std::shared_ptr, const std::string &, const std::string &, const libdar::archive_options_merge &, std::shared_ptr >()) .def(pybind11::init< const std::shared_ptr &, const libdar::path &, std::shared_ptr, const std::string &, const std::string &, const libdar::archive_options_merge & >()) .def(pybind11::init< const std::shared_ptr &, const libdar::path &, const std::string &, const std::string &, const libdar::archive_options_read &, const libdar::path &, const std::string &, const std::string &, const libdar::archive_options_repair & >()) .def("op_extract", (libdar::statistics (py_archive::*)(const libdar::path &, const libdar::archive_options_extract &)) &py_archive::op_extract) .def("op_extract", (libdar::statistics (py_archive::*)(const libdar::path &, const libdar::archive_options_extract &, std::shared_ptr)) &py_archive::op_extract) .def("summary", &libdar::archive::summary) .def("summary_data", &libdar::archive::summary_data) .def("op_diff", (libdar::statistics (py_archive::*)(const libdar::path &, const libdar::archive_options_diff &)) &py_archive::op_diff) .def("op_diff", (libdar::statistics (py_archive::*)(const libdar::path &, const libdar::archive_options_diff &, std::shared_ptr))& py_archive::op_diff) .def("op_test", (libdar::statistics (py_archive::*)(const libdar::archive_options_test &))&py_archive::op_test) .def("op_test", (libdar::statistics (py_archive::*)(const libdar::archive_options_test &, std::shared_ptr))&py_archive::op_test) .def("op_isolate", &libdar::archive::op_isolate) .def("get_children_in_table", &libdar::archive::get_children_in_table) .def("has_subdirectory", &libdar::archive::has_subdirectory) .def("get_stats", &libdar::archive::get_stats) .def("get_signatories", &libdar::archive::get_signatories) .def("init_catalogue", &libdar::archive::init_catalogue) .def("drop_all_filedescriptors", &libdar::archive::drop_all_filedescriptors) .def("set_to_unsaved_data_and_FSA", &libdar::archive::set_to_unsaved_data_and_FSA); /////////////////////////////////////////// // archives_num classes // pybind11::class_(mod, "archive_num") .def(pybind11::init(), pybind11::arg("arg") = 0); /////////////////////////////////////////// // database_archives classes // pybind11::class_(mod, "database_archives") .def("get_path", &libdar::database_archives::get_path) .def("get_basename", &libdar::database_archives::get_basename); /////////////////////////////////////////// // database_*_options classes // pybind11::class_(mod, "database_open_options") .def("clear", &libdar::database_open_options::clear) .def("set_partial", &libdar::database_open_options::set_partial) .def("set_partial_read_only", &libdar::database_open_options::set_partial_read_only) .def("set_warn_order", &libdar::database_open_options::set_warn_order); pybind11::class_(mod, "database_dump_options") .def("clear", &libdar::database_dump_options::clear) .def("set_overwrite", &libdar::database_dump_options::set_overwrite); pybind11::class_(mod, "database_add_options") .def("clear", &libdar::database_add_options::clear); pybind11::class_(mod, "database_remove_options") .def("clear", &libdar::database_remove_options::clear) .def("set_revert_archive_numbering", &libdar::database_remove_options::set_revert_archive_numbering); pybind11::class_(mod, "database_change_basename_options") .def("clear", &libdar::database_change_basename_options::clear) .def("set_revert_archive_numbering", &libdar::database_change_basename_options::set_revert_archive_numbering); pybind11::class_(mod, "database_change_path_options") .def("clear", &libdar::database_change_path_options::clear) .def("set_revert_archive_numbering", &libdar::database_change_path_options::set_revert_archive_numbering); pybind11::class_(mod, "database_restore_options") .def("clear", &libdar::database_restore_options::clear) .def("set_early_release", &libdar::database_restore_options::set_early_release) .def("set_info_details", &libdar::database_restore_options::set_info_details) .def("set_extra_options_for_dar", &libdar::database_restore_options::set_extra_options_for_dar) .def("set_ignore_dar_options_in_database", &libdar::database_restore_options::set_ignore_dar_options_in_database) .def("set_date", &libdar::database_restore_options::set_date) .def("set_even_when_removed", &libdar::database_restore_options::set_even_when_removed); pybind11::class_(mod, "database_used_options") .def("clear", &libdar::database_used_options::clear) .def("set_revert_archive_numbering", &libdar::database_used_options::set_revert_archive_numbering); /////////////////////////////////////////// // database classes // pybind11::class_(mod, "database") .def(pybind11::init &>()) .def(pybind11::init< const std::shared_ptr &, const std::string &, const libdar::database_open_options & >()) .def("dump", &libdar::database::dump) .def("add_archive", &libdar::database::add_archive) .def("remove_archive", &libdar::database::remove_archive) .def("set_permutation", &libdar::database::set_permutation) .def("change_name", &libdar::database::change_name) .def("set_path", &libdar::database::set_path) .def("set_options", &libdar::database::set_options) .def("set_dar_path", &libdar::database::set_dar_path) .def("set_compression", &libdar::database::set_compression) .def("get_contents", &libdar::database::get_contents) .def("get_options", &libdar::database::get_options) .def("get_dar_path", &libdar::database::get_dar_path) .def("get_compression", &libdar::database::get_compression) .def("get_database_version", &libdar::database::get_database_version) .def("get_files", &libdar::database::get_files) .def("get_version", &libdar::database::get_version) .def("show_most_recent_stats", &libdar::database::show_most_recent_stats) .def("restore", &libdar::database::restore) .def("check_order", &libdar::database::check_order); /////////////////////////////////////////// // libdar_xform classes // pybind11::class_(mod, "libdar_xform") .def(pybind11::init< const std::shared_ptr &, const std::string &, const std::string &, const std::string &, const libdar::infinint &, const std::string & >()) .def(pybind11::init< const std::shared_ptr &, const std::string & >()) .def("xform_to", (void (libdar::libdar_xform::*)(const std::string &, const std::string &, const std::string &, bool, bool, const libdar::infinint &, const libdar::infinint &, const libdar::infinint &, const std::string &, const std::string &, const std::string &, libdar::hash_algo, const libdar::infinint &, const std::string &)) &libdar::libdar_xform::xform_to) .def("xform_to", (void (libdar::libdar_xform::*)(int, const std::string &)) &libdar::libdar_xform::xform_to); /////////////////////////////////////////// // libdar_slave classes // pybind11::class_(mod, "libdar_slave") .def(pybind11::init< std::shared_ptr &, const std::string &, const std::string &, const std::string &, bool, const std::string &, bool, const std::string &, const std::string &, const libdar::infinint & >()) .def("run", &libdar::libdar_slave::run); } dar-2.7.15/src/gettext.h0000644000175000017500000000635614636066467011755 00000000000000/* Convenience header for conditional use of GNU . Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _LIBGETTEXT_H #define _LIBGETTEXT_H 1 /* NLS can be disabled through the configure --disable-nls option. */ #if ENABLE_NLS /* Get declarations of GNU message catalog functions. */ #if HAVE_GETTEXT #if HAVE_LIBINTL_H #include #endif #endif #else /* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which chokes if dcgettext is defined as a macro. So include it now, to make later inclusions of a NOP. We don't include as well because people using "gettext.h" will not include , and also including would fail on SunOS 4, whereas is OK. */ #if defined(__sun) # include #endif /* Disabled NLS. The casts to 'const char *' serve the purpose of producing warnings for invalid uses of the value returned from these functions. On pre-ANSI systems without 'const', the config.h file is supposed to contain "#define const". */ # define gettext(Msgid) ((const char *) (Msgid)) # define dgettext(Domainname, Msgid) ((const char *) (Msgid)) # define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) # define ngettext(Msgid1, Msgid2, N) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define dngettext(Domainname, Msgid1, Msgid2, N) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define textdomain(Domainname) ((const char *) (Domainname)) # define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) # define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) // this is a DAR addition to workaround // standard C++ header files (Redhat distro) that include /usr/include/libintl.h // in any case. This should let this inclusion ineffective #define _LIBINTL_H #endif /* A pseudo function call that serves as a marker for the automated extraction of messages, but does not call gettext(). The run-time translation is done at a different place in the code. The argument, String, should be a literal string. Concatenated strings and other string expressions won't work. The macro's expansion is not parenthesized, so that it is suitable as initializer for static 'char[]' or 'const char[]' variables. */ #define gettext_noop(String) String #endif /* _LIBGETTEXT_H */ dar-2.7.15/src/check/0000755000175000017500000000000014640025216011221 500000000000000dar-2.7.15/src/check/loop.sh0000755000175000017500000000566514636066467012507 00000000000000#!/bin/sh ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### for archive in Old_format/* ; do basename=`echo "$archive" | sed -r -e "s/\.[0-9]+\.dar//"` echo "Testing ascendant compatibility with $basename" crypto=`echo "$basename.1.dar" | grep _crypto_| wc -l` if [ $crypto -gt 0 ] ; then KEY="-K bf:test" else KEY="-q" fi if ! ../dar_suite/dar -q -Q -t "$basename" $KEY ; then echo "FAILED TESTING OLD ARCHIVE FORMAT: $basename" exit 1 fi done echo "exit" | expect || exit 1 TMP_FILE=my_tmpfile MY_MAKEFILE=my_makefile rm -f $TMP_FILE $MY_MAKEFILE printf "Building the Makefile (patience, this takes some minutes)... " for multi_thread in 2 1 ; do for hash in md5 none sha1 ; do for crypto in bf none scram aes twofish serpent camellia ; do for zip in zstd lz4 xz gzip none bzip2 lzo ; do for zipbs in 1234 0 ; do for slice in 1k none ; do for Slice in 500 none ; do for tape in y n ; do for seq_read in y n ; do for digit in 3 none ; do for sparse_size in 100 0 ; do for keep_compr in y n ; do for recheck_hole in y n ; do for asym in y n ; do TARGET=target-$multi_thread-$hash-$crypto-$zip-$zipbs-$slice-$Slice-$tape-$seq_read-$digit-$sparse_size-$keep_compr-$recheck_hole-$asym cat >> $MY_MAKEFILE <> $TMP_FILE <> $MY_MAKEFILE echo `cat $TMP_FILE` >> $MY_MAKEFILE rm $TMP_FILE echo "Done" dar-2.7.15/src/check/sftp_mdelete0000755000175000017500000000303014636066467013560 00000000000000#!/bin/bash ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### if [ -z "$1" -o -z "$2" ] ; then echo "usage: $0 " echo "will remove pattern in the directory pointed to by the URL" exit 1 fi script="=$2" echo "script = $script" echo "$1" | sed -r -n -e 's#^sftp://([^:@/]+):([^:@/]+)@([^:@/]+)(.*)$#spawn sftp \1@\3\nexpect "*password:"\nsend "\2\\n"\nexpect "sftp>"\nsend \"cd \4\\n"#p' > "$script" echo "expect 'sftp>'" >> "$script" echo "send \"rm $2\\n\"" >> "$script" echo "expect 'sftp>'" >> "$script" echo "send \"exit\\n\"" >> "$script" cat "$script" expect -f "$script" rm "$script" dar-2.7.15/src/check/modif_tree.sh0000755000175000017500000000255314636066467013644 00000000000000#!/bin/sh ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### if [ $# -ne 2 ] ; then echo "usage: $0 " exit 1 fi SUB1=S"$2"B1 SUB2=S"$2"B2 SUB3=S"$2"B3 cd "$1" cd "$SUB1" ln ../plain_file.txt new_hard_linked_inode.txt echo "new file alone" > plain2.txt echo "trigger binary patch" >> for_binary_delta cd .. cd "$SUB2" rm -f symlink.txt rm -f tube1 cd .. rm -rf "$SUB3" cd "$SUB1" rm blockdev chgrp sys plain_file2.txt dar-2.7.15/src/check/Old_format/0000755000175000017500000000000014640025216013307 500000000000000dar-2.7.15/src/check/Old_format/archive_09_nozip.1.dar0000644000175000017500000001016514636066467017253 00000000000000{‡¯W`¬vTT€€ ‡¯W`¬v090nN/A€A&­ýêw!D‡¯W`¬v­ýêw!Fddirectory_1€è€èíu€`W­é€Us€W.u€`W¦|€à€_z€ë­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fslog€è€è¾s€Whgs€W ¡×u€`Wž½€9B€w€ñß­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@‡ EVERYONE@­ýêw!r€6O­ýêw!Ffplain text 2.txt€è€è¼s€[^v›s€W|®u€`Wž½€)¢€w€9n€wHello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Flsymlink€è€èÿs€W~ s€W}¬u€`Wž½€ b../plain text.txt€{­ýêw!Ffsparse_file€è€è¤s€W4s€W.u€`Wž½€ b€_€n€ñX®ýêw!F€­ýêw!R€­ýêw!E€system.nfs4_acl€P‡OWNER@GROUP@ EVERYONE@­ýêw!r€ÏKR­ýêw!Fpnamed_pipe€è€è¼s€W|Ðs€W|Ðu€`Wž½€€w€v­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Fdempty_subdir€è€èýu€`W­é€=5s€W}Åu€`W¦|€ @€_z€ŠT­ýêw!E€system.nfs4_acl€PçOWNER@çGROUP@¡ EVERYONE@­ýêw!r€ÏmR­ýêw!Fz€z­ýêw!Fz€z­ýêw!Ffplain text.txt€è€è¼u€`Wžë€ÊSs€W|qu€`Wž½€}€w€3n€8Hello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Fddirectory_2€è€èíu€`W­é€LÕs€W}Åu€`W¦|€*€€_z€°;­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fslog€è€è¶s€Whgs€W ¡×u€`Wž½€¼B€_€ðz­ýêw!E€system.nfs4_acl€P‡OWNER@‡GROUP@‡ EVERYONE@­ýêw!r€ÏKR­ýêw!Ffplain text 2.txt€è€è¼u€`Wžë€Ùós€W|®u€`Wž½€¬¢€w€9n€s—Hello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Flsymlink€è€èÿu€`Wžë€Ùós€W}¬u€`Wž½€b../plain text.txt€eS­ýêw!Fpnamed_pipe€è€è¤s€W|Ðs€W|Ðu€`Wž½€€_€Á­ýêw!E€system.nfs4_acl€P‡OWNER@GROUP@ EVERYONE@­ýêw!r€ÏKR­ýêw!Fdempty_subdir€è€èíu€`W­é€ls€W}Åu€`W¦|€*€€_z€ U­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fz€z­ýêw!Fz€z­ýêw!C‡¯W`¬vdroot€€s€s€`Wž½s€ddirectory_1€è€èíu€`W­é€Us€W.u€`W¦|€à€_€p€Ï+Rslog€è€è¾s€Whgs€W ¡×u€`Wž½€9B€w€*€6Ofplain text 2.txt€è€è¼s€[^v›s€W|®u€`Wž½€)¢€w€X€0O€9€ €9n€9}lsymlink€è€èÿs€W~ s€W}¬u€`Wž½€ b../plain text.txtfsparse_file€è€è¤s€W4s€W.u€`Wž½€ b€_€Ÿ€ÏKR€€€ n€pnamed_pipe€è€è¼s€W|Ðs€W|Ðu€`Wž½€€w€`€0Odempty_subdir€è€èýu€`W­é€=5s€W}Åu€`W¦|€ @€_€A€ÏmRzzfplain text.txt€è€è¼u€`Wžë€ÊSs€W|qu€`Wž½€}€w€p€0O€3€(€3n€%;ihddirectory_2€è€èíu€`W­é€LÕs€W}Åu€`W¦|€*€€_€P€Ï+Rslog€è€è¶s€Whgs€W ¡×u€`Wž½€¼B€_€ €ÏKRfplain text 2.txt€è€è¼u€`Wžë€Ùós€W|®u€`Wž½€¬¢€w€ %€0O€9€×€9n€9}lsymlink€è€èÿu€`Wžë€Ùós€W}¬u€`Wž½€b../plain text.txtpnamed_pipe€è€è¤s€W|Ðs€W|Ðu€`Wž½€€_€ O€ÏKRdempty_subdir€è€èíu€`W­é€ls€W}Åu€`W¦|€*€€_€ €Ï+Rzzz€Šô‚J€ ³À090nN/A€€Ð.€/ÀTdar-2.7.15/src/check/Old_format/archive_08-1.1.dar0000644000175000017500000000575214636066467016177 00000000000000{µ„Wr6TT€€ µ„Wr6081ycreated on terre at Tue Apr 26 17:09:41 2016 with "dar/src/dar_suite/dar" "-c" "archive_08" "-R" "Tree" "-zlzo"€9P­ýêw!Dµ„Wr6­ýêw!Fddirectory_1€€í€W/€W.€W.z€™÷­ýêw!Fbblock_device€€¼€W|ý€W|ý€W~u€C€Å5­ýêw!EBZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€­ýêw!r€ô°#G­ýêw!Fcchar_device€€¼€W}€W}€W~u€C€ÐP­ýêw!EBZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€­ýêw!r€ô°#G­ýêw!Flsymlink€€ÿ€W~ €W}¬€W~../plain text.txt€»f­ýêw!Ffsparse_file€€¤€W4€W.€W.€y€¹MBZh91AY&SY½9ˆtÑÀ@ €@ 1LA“vˆ^‚îH§ §!Ñ­ýêw!R€­ýêw!Fmplain text 2.txt€>fplain text 2.txt€€¼€W|¬€W|®€W~u€C€9n€GšHello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!EBZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€­ýêw!r€ô°#G­ýêw!Fdempty_subdir€€ý€W}Å€W}Å€W~u€]z€¹u­ýêw!EBZh91AY&SY€á–à/÷Àö€À@"«fß`@ T5Cɨõ4¡£Fj OJ6¡ ¦PŒH@åùÁjƒÔäì´—Þ@tFÉ9G7º‘×¹YH$Ȋݱ¡‹ÍI¤åpÒdcU`~.äŠp¡!Ã-À­ýêw!r€ùâ+­ýêw!Fz€z­ýêw!Fpnamed_pipe€€¼€W|ЀW|ЀW~u€C€”h­ýêw!EBZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€­ýêw!r€ô°#G­ýêw!Fslog€€¾€Whg€W ¡×€W~u€C€x¼­ýêw!EBZh91AY&SYÞšºÜ sÀõ€@@ª&Ì`@ @ÓJzž£M4SOHP4 ™ $¨GÂæôÒŽßaBÂ¥DOC¿ ­O³v–Ç̓Ȁø»’)„†ôÕÖà­ýêw!r€ô°#G­ýêw!Fz€z­ýêw!Ffplain text.txt€€¼€W|Ÿ€W|q€W~u€]€3n€_¸Hello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!EBZh91AY&SY VÿH/÷Àõ€À@"«fß`@ T5Cɨõ4£Fj OJ‘êhhГ$ rüYïj”ä­±ŠÞ@tÉ(Ã(Ï>¢aܬ§ 2"¶ÐſФÒr¸i¼ÙÇÀUX‹¹"œ(H«¤­ýêw!r€øâ+­ýêw!Fddirectory_2€€í€W}Å€W}Å€W}Åz€š­ýêw!Fbblock_device€€¤€W|ý€W|ý€W|ý€½­ýêw!Fcchar_device€€¤€W}€W}€W}€£Š­ýêw!Flsymlink€€ÿ€W}¬€W}¬€W}¬../plain text.txt€·f­ýêw!Fmplain text 2.txt€X€ë]­ýêw!Fdempty_subdir€€í€W}Å€W}Å€W}Åz€¿­ýêw!Fz€z­ýêw!Fpnamed_pipe€€¤€W|ЀW|ЀW|ЀOÍ­ýêw!Fslog€€¶€Whg€W ¡×€W}O€Bv­ýêw!Fz€z­ýêw!CBZh91AY&SY83kM ÿÿ0@AÊ ½k€€Î¿ïÿwĬ%C‚P€ b°˜PÒM=Fi0&¢2 Qè`L„õ ƒF€2iˆi È1 h ˜ S&§‘6šSjžˆhz 4z€<¡¦ž„ôM4dh3$ìA б` ¡±‰ ¤6“M6=~Š!ƒ)´¼Y$B"Öµ­å-Û@]i(7ÙëÄA‚ÄÄ"¦#33!½¦FHÌL² ~^=“<ÚŒdGæ6‰4?±5LD©ŽJƒ1Ž÷†4Ûa °(âð_»† °œ¼Þ4Bý6õÂr£%4 •|+7ËbØû|4Ô_.YNÜÏœUE!Õ'")ê5 Ì$çX¨Õ&€È—BË è”°ªð÷ ŸK”+…Ü¿›—JN"ÛŒV…Y¦zRÀÅxhêS»³ŸÐ#ØÁ,)ŠÄ;Œb¾”ÉÁ<,x3µ­aiQr-!üCê0Èy¿†w²2ù†}a>°„® Þú`s Ä9åÚ›–”€eÛîm¶¿Ïx¿Œ1ñJÓéG“¢P$¹*>žs›¤¹2‘)B Ÿñw$S… ƒ6´Ð€ JÀ081ycreated on terre at Tue Apr 26 17:09:41 2016 with "dar/src/dar_suite/dar" "-c" "archive_08" "-R" "Tree" "-zlzo"€}€ÄX€ 8ÀTdar-2.7.15/src/check/Old_format/archive_09.1.dar0000644000175000017500000000761514636066467016042 00000000000000{µWí4TT€€ µWí4090xN/A€W&­ýêw!DµWí4­ýêw!Fddirectory_1€€ín€W/€$üÎRn€W.€ 8Btn€W.€ 8Bt€€1z€0×­ýêw!S€ lbaFlbbFlbcFlbdFlbeFlbfFlbgFlbhFlbiFlbjFlbkFlblF­ýêw!s€Œ ­ýêw!Fbblock_device€€¼n€W|ý€‘n€W|ý€‘n€W~u€7°h¼€C€4y­ýêw!Eý7zXZi"Þ6!ÏXÌàM:]@0&ú+‡%r´’¡Õâ“;bG~xÅ ÅZÄ¥ÃL<[çtÒZÝvØ':µ!‡;îUlØË“ʧ§¼_lRNjæo9B™ YZ­ýêw!r€ô°#G­ýêw!Fcchar_device€€¼n€W}€ á¥Øn€W}€ á¥Øn€W~u€7°h¼€C€Š·­ýêw!Eý7zXZi"Þ6!ÏXÌàM:]@0&ú+‡%r´’¡Õâ“;bG~xÅ ÅZÄ¥ÃL<[çtÒZÝvØ':µ!‡;îUlØË“ʧ§¼_lRNjæo9B™ YZ­ýêw!r€ô°#G­ýêw!Flsymlink€€ÿn€W~ €2íyn€W}¬€ ×Mðn€W~€.7-¤../plain text.txt€6T­ýêw!Ffsparse_file€€¤n€W4€1$×òn€W.€ uKNn€W.€ uKN€€1€x€ ý7zXZi"Þ6!ÏXÌ ®ýêw!F€Ì|¸9 =bzB™ YZ­ýêw!R€­ýêw!S€ lbaFlbbFlbcFlbdFlbeFlbfFlbgFlbhFlbiFlbjFlbkFlblF­ýêw!s€Œ ­ýêw!Fmplain text 2.txt€>fplain text 2.txt€€¼n€W|¬€!‰ än€W|®€C¾n€W~u€7°h¼€C€€1€9n€F¨Hello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!Eý7zXZi"Þ6!ÏXÌàM:]@0&ú+‡%r´’¡Õâ“;bG~xÅ ÅZÄ¥ÃL<[çtÒZÝvØ':µ!‡;îUlØË“ʧ§¼_lRNjæo9B™ YZ­ýêw!r€ô°#G­ýêw!S€ lbaFlbbFlbcFlbdFlbeFlbfFlbgFlbhFlbiFlbjFlbkFlblF­ýêw!s€Œ ­ýêw!Fdempty_subdir€€ýn€W}Å€ Æn€W}Å€ Æn€W~u€7°h¼€]€€1z€®ü­ýêw!Eý7zXZi"Þ6!ÏXÌàmT]@0Fú+‡%r´’¡Õâ“;bG~xÅ ÅZÄ¥ÃL;NÀ|zÖðGñöº![\ (p…åºë3Þ,9œÖwñ±”ÆÛ¹ZLgÀG̰"6`È]»…lnßÝÄCB™ YZ­ýêw!r€ùâ+­ýêw!S€ lbaFlbbFlbcFlbdFlbeFlbfFlbgFlbhFlbiFlbjFlbkFlblF­ýêw!s€Œ ­ýêw!Fz€z­ýêw!Fpnamed_pipe€€¼n€W|Ѐ µhn€W|Ѐ µhn€W~u€7°h¼€C€”Õ­ýêw!Eý7zXZi"Þ6!ÏXÌàM:]@0&ú+‡%r´’¡Õâ“;bG~xÅ ÅZÄ¥ÃL<[çtÒZÝvØ':µ!‡;îUlØË“ʧ§¼_lRNjæo9B™ YZ­ýêw!r€ô°#G­ýêw!Fslog€€¾n€Whg€ °KXn€W ¡×€°ú€n€W~u€7°h¼€C€ÝÓ­ýêw!Eý7zXZi"Þ6!ÏXÌàM8]@0&ú+‡%r´’¡Õâ“;bG~xÅ ÅZÄ¥ÃL<[çtÒZÝvØ':µ!ëX…¶°s˜z¾,ÔÇPNè„Y B™ YZ­ýêw!r€ô°#G­ýêw!Fz€z­ýêw!Ffplain text.txt€€¼n€W|Ÿ€,ü•~n€W|q€cœ«n€W~u€7°h¼€]€€1€3n€Hello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!Eý7zXZi"Þ6!ÏXÌàmU]@0Fú+‡%r´’¡Õâ“;bG~xÅ ÅZÄ¥ÃL;NÀ|zšèRVn[û‹Ù(·ö¾Á}©‹²\Ä—g{Δ¦>ÃÈüѺCC!´ÍÖrPY^Р÷1ðPmnžìßZB™ YZ­ýêw!r€øâ+­ýêw!S€ lbaFlbbFlbcFlbdFlbeFlbfFlbgFlbhFlbiFlbjFlbkFlblF­ýêw!s€Œ ­ýêw!Fddirectory_2€€ín€W}Å€+&õ#n€W}Å€ ÆÎn€W}Å€ Æ΀€1z€BÑ­ýêw!S€ lbaFlbbFlbcFlbdFlbeFlbfFlbgFlbhFlbiFlbjFlbkFlblF­ýêw!s€Œ ­ýêw!Fbblock_device€€¤n€W|ý€““n€W|ý€““n€W|ý€““€=r­ýêw!Fcchar_device€€¤n€W}€ á§`n€W}€ á§`n€W}€ á§`€¾V­ýêw!Flsymlink€€ÿn€W}¬€-Œè|n€W}¬€ ×On€W}¬€ ×O../plain text.txt€Õß­ýêw!Fmplain text 2.txt€X€ë]­ýêw!Fdempty_subdir€€ín€W}Å€ ÆÎn€W}Å€ ÆÎn€W}Å€ Æ΀€1z€ j­ýêw!S€ lbaFlbbFlbcFlbdFlbeFlbfFlbgFlbhFlbiFlbjFlbkFlblF­ýêw!s€Œ ­ýêw!Fz€z­ýêw!Fpnamed_pipe€€¤n€W|Ѐ ¹n€W|Ѐ ¹n€W|Ѐ ¹€_0­ýêw!Fslog€€¶n€Whg€ °KXn€W ¡×€°ú€n€W}O€6ý€x­ýêw!Fz€z­ýêw!Cý7zXZi"Þ6!ÏXÌàý%]ZŸ¿åD†•²!Eù*L«x„}š:HUÜX¼ï`ƒ-ð¿Zjá:8Œd®ùfnääf›IãÞµÆÍK[(–*‰÷„2=ì†ÔI fMv¯ãÄ:ÐÕŠ„žÅHl¾FdzŽDkF®¡ZKPžf Û[HàÞsßÐÑÍ$^¨0óvT•I3·7Ù‰>¶° ôH’ºíÖ*SÇùÉ.ß±¹ÔqQk‡ ¸é¢†˜Ó2 VöXèY¨ mÈ1U¸™.Ì“àÆQ>*0»«ÂqîÜLZfƒš¢æc¥óÀ:õx.#€Ã Hy/ù2ºP‚êû®_?/y=°é]ÿß(„&iùß_À> bÞ¦òÍEjS”‰Ùª9[§Ü2 &Gð…î»48öÊ<õŽ Ë‚[£æº«B†BQí£,æÚ†ÄCáéþc ªéãã;‡V:äÆÓeÍaé *’0-ÝêÞ)æòm•âÔÔÄf™ÉR<¢©ÝÄBïÌcT±³>ÝÕQ ½¢ÓRÚr®_#ª“‰ß´n úµPƒ0ÜÜõZ®ÆOæÙÒmýúÊ«‹ÏÚ§ÕX *Û‡‡K÷ï® hÓ4Eö‹¯\¸ú?@§ÿOæ}Fz $f—S-¦°Xä†\‰¶QóL§c›€c¾†%+:±ªMµÿ}¹“` ŸGtÁSr>À%Æ{™ Ãt*¾>l$·Þå®T<Á>ÃW2b¢ßõl­Í³Ö½þ Ãümú>0 ‹YZ€ ÚÀ090xN/A€€Æ.€GÀTdar-2.7.15/src/check/Old_format/archive_08-1_nozip.1.dar0000644000175000017500000000751414636066467017414 00000000000000{0®W`DTT€€ 0®W`D081nN/A€@'­ýêw!D0®W`D­ýêw!Fddirectory_1€è€èí€`W­é€W.€`W¦|€_z€œ°­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fslog€è€è¾€Whg€W ¡×€`Wž½€w€´­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@‡ EVERYONE@­ýêw!r€6O­ýêw!Ffplain text 2.txt€è€è¼€[^v›€W|®€`Wž½€w€9n€¢¼Hello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Flsymlink€è€èÿ€W~ €W}¬€`Wž½../plain text.txt€ Z­ýêw!Ffsparse_file€è€è¤€W4€W.€`Wž½€_€n€vE®ýêw!F€­ýêw!R€­ýêw!E€system.nfs4_acl€P‡OWNER@GROUP@ EVERYONE@­ýêw!r€ÏKR­ýêw!Fpnamed_pipe€è€è¼€W|ЀW|Ѐ`Wž½€w€œ­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Fdempty_subdir€è€èý€`W­é€W}Å€`W¦|€_z€X»­ýêw!E€system.nfs4_acl€PçOWNER@çGROUP@¡ EVERYONE@­ýêw!r€ÏmR­ýêw!Fz€z­ýêw!Fz€z­ýêw!Ffplain text.txt€è€è¼€`Wžë€W|q€`Wž½€w€3n€œïHello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Fddirectory_2€è€èí€`W­é€W}Å€`W¦|€_z€žX­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fslog€è€è¶€Whg€W ¡×€`Wž½€_€”­ýêw!E€system.nfs4_acl€P‡OWNER@‡GROUP@‡ EVERYONE@­ýêw!r€ÏKR­ýêw!Ffplain text 2.txt€è€è¼€`Wžë€W|®€`Wž½€w€9n€qÅHello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Flsymlink€è€èÿ€`Wžë€W}¬€`Wž½../plain text.txt€ ­ýêw!Fpnamed_pipe€è€è¤€W|ЀW|Ѐ`Wž½€_€¬­ýêw!E€system.nfs4_acl€P‡OWNER@GROUP@ EVERYONE@­ýêw!r€ÏKR­ýêw!Fdempty_subdir€è€èí€`W­é€W}Å€`W¦|€_z€H»­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fz€z­ýêw!Fz€z­ýêw!C0®W`Ddroot€€€€`Wž½€ddirectory_1€è€èí€`W­é€W.€`W¦|€_€c€Ï+Rslog€è€è¾€Whg€W ¡×€`Wž½€w€€6Ofplain text 2.txt€è€è¼€[^v›€W|®€`Wž½€w€;€0O€9€í€9n€9}lsymlink€è€èÿ€W~ €W}¬€`Wž½../plain text.txtfsparse_file€è€è¤€W4€W.€`Wž½€_€r€ÏKR€€R€ n€pnamed_pipe€è€è¼€W|ЀW|Ѐ`Wž½€w€+€0Odempty_subdir€è€èý€`W­é€W}Å€`W¦|€_€ÿ€ÏmRzzfplain text.txt€è€è¼€`Wžë€W|q€`Wž½€w€!€0O€3€Ù€3n€%;ihddirectory_2€è€èí€`W­é€W}Å€`W¦|€_€ô€Ï+Rslog€è€è¶€Whg€W ¡×€`Wž½€_€¦€ÏKRfplain text 2.txt€è€è¼€`Wžë€W|®€`Wž½€w€´€0O€9€f€9n€9}lsymlink€è€èÿ€`Wžë€W}¬€`Wž½../plain text.txtpnamed_pipe€è€è¤€W|ЀW|Ѐ`Wž½€_€ É€ÏKRdempty_subdir€è€èí€`W­é€W}Å€`W¦|€_€ …€Ï+Rzzz€pÀ›€ À081nN/A€€Ñ/€ÀTdar-2.7.15/src/check/Old_format/archive_05.1.dar0000644000175000017500000000245114636066467016027 00000000000000{”‹WŽ5TN05yN/ABZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SY8Wå@À 0Ì)¦Ä .äŠp¡ p®9ÊHello Word! This is a plain file with several hard links BZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SYþ…/÷Àö€À@"«fß`@ T5#Ѩõ2i£Ò OJ6¡ ¦PŒH@åùÁjƒÔäì´—Þ@tFÉ9G7º‘Ï¿-]â"»H+F 4Bª¹%E©òÊËü]ÉáBCùübBZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SYÞšºÜ sÀõ€@@ª&Ì`@ @ÓJzž£M4SOHP4 ™ $¨GÂæôÒŽßaBÂ¥DOC¿ ­O³v–Ç̓Ȁø»’)„†ôÕÖàHello Word! This is a plain file without hard link BZh91AY&SYãÄ¡º/÷Àõ€À@"«fß`@ T5#Ѩõ2hƒÒ OJ‘êhhГ$ rüYïj”ä­±ŠÞ@tÉ(Ã(Ï>¢g×v®Çñ]8+F 4Bª¹%âÔøg 2À?rE8PãÄ¡ºBZh91AY&SY˜bØÑÿû0ÀÀ¼"€¿ÿÿwÀ ¤P€°Qda)£OA¦šžž¡4ɦš Í'¤z˜&ƒÔÞ©é‰)6“Ò2 €iê2ÐÈi£Ô¨Œ˜F24 "ÓeñI ÐÛ€ccAe•Ùx¸¨»Íýb&Ê$L[­oˆ¯1a ¡ Ïh_•|BH*—‘Þª{'…\Æ8ø-=¢µGå¥gdHLFÅ*߈é"fB[wÌ„f`ÎV¤Q(6´°Š#zÜD' he„âLB[÷Ð")bÂH¶88<0L$Š *"Cž]1ÑXâ”a ×§ˆ„PÅÕ »=H £…@u!lÀƒñ‚†¤„ÇK5ý°î˜@(nŠ»+_3½²z¼µ¶|Ú÷?[;IŸäB×µýpo®”œÞMyû#×’J7ehè~7 @¶$)-SMßÙÕžp’ÿrE8P˜bØ€‚Àdar-2.7.15/src/check/Old_format/archive_02.1.dar0000644000175000017500000000264214636066467016026 00000000000000{1081815329TN02z-c archive_02 -R A -z xÚóHÍÉÉWÏ/JQä ÉÈ,V¢D…‚œÄÌ<…´ÌœT…òÌ’ŒüÒ…ŒÄ¢…œÌ¼l.ÆÄ°xÚóHÍÉÉWÏ/JQä ÉÈ,V¢D…‚œÄÌ<…´ÌœT…òÌ’ …âԲԢąŒÄ¢…œÌ¼ìb.8ÝxÚíÁ1 õOm_ >ðxÚK)ÊÏ/a`fƒ8‘V“˜™§P’ZQ¢WRTÀü‚ù㞆踲٠áò5… UÆ BDXú§¤d¥&—äUÆA•¿…)¯=šS\™›“™— •ù—Y£§§fWJjnAIe|qiÐHL£ª òsSSâ 2 R¡²K@Nº&R†)auú:°ƒAD(ˆ°·Mû¸8'?ªv[C¸\FzC8×ÂëUHÞ2DwK½^ZqAbQqj|ZfÂ-pÙHpN,ü ”†Â_¼¡° ¡€îÇ}H~¬IÆ´Š€£Àdar-2.7.15/src/check/Old_format/archive_06.1.dar0000644000175000017500000000244514636066467016033 00000000000000{,‰TN06yN/ABZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SY8Wå@À 0Ì)¦Ä .äŠp¡ p®9ÊHello Word! This is a plain file with several hard links BZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SYþ…/÷Àö€À@"«fß`@ T5#Ѩõ2i£Ò OJ6¡ ¦PŒH@åùÁjƒÔäì´—Þ@tFÉ9G7º‘Ï¿-]â"»H+F 4Bª¹%E©òÊËü]ÉáBCùübBZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SYÞšºÜ sÀõ€@@ª&Ì`@ @ÓJzž£M4SOHP4 ™ $¨GÂæôÒŽßaBÂ¥DOC¿ ­O³v–Ç̓Ȁø»’)„†ôÕÖàHello Word! This is a plain file without hard link BZh91AY&SYãÄ¡º/÷Àõ€À@"«fß`@ T5#Ѩõ2hƒÒ OJ‘êhhГ$ rüYïj”ä­±ŠÞ@tÉ(Ã(Ï>¢g×v®Çñ]8+F 4Bª¹%âÔøg 2À?rE8PãÄ¡ºBZh91AY&SY»‚ø¼Ïÿÿû0ÀÀ¼"€¿ÿÿwÀ€¤P€°Qda(Ó'¦$ö¢4z4ôžõ§£Õ= ‘%#ÒmG¤4i ÐhÉ4Èéª#&Ñ£@I&›0ŠH`(†Ø4 ;/jåÞzh‰³A‰ÖªÚã'˜¡ ¡ ¿`a•ÂÄBH2ËÈ—™lÅ•]Ž$> h«Q÷øA4hy—eU½Ø>N„†Bˆôp™ Ó(Nf7IÔ¾ ›XE½N ¢3²Âq‡FLBZÂ#I‚ÂH688>0M% *"Eß]1áWdÐ4ˆ›×hˆQ¬¶C‚«P‘žÆÂ2ÎDu h^AP„`ù-ÊùH€<î]µß¡Ö ÐgªÖ yþ›I¥Õ8¦Èç¤Ȇ3îÆ»‰Y²ö°îÕ™H8-zgÔ>•a(̃s!¥M7k-Ôd“…Eþ.äŠp¡!wñx€‚Àdar-2.7.15/src/check/Old_format/archive_04.1.dar0000644000175000017500000000240314636066467016023 00000000000000{KWèTTN04yN/AÀBZh91AY&SY.Ðû§óÀõ€@@ª$È@@ P i¡‘“i¥Ièš6™E’T#R³ÚÎÁŒß·= AèûDÜÔqSX"ø»’)„v‡Ý8BZh91AY&SY.Ðû§óÀõ€@@ª$È@@ P i¡‘“i¥Ièš6™E’T#R³ÚÎÁŒß·= AèûDÜÔqSX"ø»’)„v‡Ý8BZh91AY&SY8Wå@À 0Ì)¦Ä .äŠp¡ p®9ÊHello Word! This is a plain file with several hard links BZh91AY&SY.Ðû§óÀõ€@@ª$È@@ P i¡‘“i¥Ièš6™E’T#R³ÚÎÁŒß·= AèûDÜÔqSX"ø»’)„v‡Ý8BZh91AY&SYô¸è*wÀö€À@"«dÏ@@ T5&ÂbžƒS҇ꆣAèß’A;|n@Úß{ŽD©!È›o*<Ã÷C«ðKÑÕMèÔ3Ã{a èäƒÀ 6.äŠp¡ %éqÐBZh91AY&SY.Ðû§óÀõ€@@ª$È@@ P i¡‘“i¥Ièš6™E’T#R³ÚÎÁŒß·= AèûDÜÔqSX"ø»’)„v‡Ý8BZh91AY&SYûD“óÀõ€@@ª$È@@ @ÓJ6£ÑOj…M Œ˜D¸hOhž&¸“¸Áf^rL•­•,Àü]ÉáBCíLHello Word! This is a plain file without hard link BZh91AY&SYO×*wÀõ€À@"«dÏ@@ T5&ÄÁ j OJQê4¨ŸIa»3Ü ØžùŽZd p(›gX ŽªuĽ”ÞC<6Œ!=yâîH§ éà:àBZh91AY&SY÷e?žÐÿÿù0@Àƒ¼&€¿ïÿwȤ…"@€@°QbД”dѦ¦4i“LšmF‘êz4L†F™=G©ˆýPH’G©4z@†š4  TE&jž›Qš)µ210†¤d A¦ ’ M6^ÀP °bM¦ØÐRÌ]-«›»ß´Ä𤉄ìÖ³²`ÎÃm\¡z. ¤‚‚ê)£´Ï9êØc‰\´NOŠͽQ"«ÊW.1ƒÅ Ä&Wdè!2De z%Ñg„¤\ñG’w6§Ú¢F(¾pf çA²¨P@Å‘Ž tH ’B'¸FÇK´Yâé’6”A7Óð 3Ž$ h%9Ð0ê$`s ¤)“ õ5Ý—8€pÝŠíË+dlê“’Õ-[Ð}K$§EsÞXäF)áWû}•¥|7ïÔ¯ôTäÒ_+‹Ø <É;q'n¯~öD‚€‹[aü8Ò4µÍœ Oñw$S… vSùà€TÀdar-2.7.15/src/check/Old_format/archive_03_nozip.1.dar0000644000175000017500000400134414636066467017250 00000000000000{I¦W`æ6TN03nN/AHello Word! This is a plain file with several hard links Hello Word! This is a plain file without hard link Hello Word! This is a plain file with several hard links droot€€ddirectory_1èèí€`Wžë€W.slogèè¾€Whg€W ¡×fplain text 2.txtèè¼€[^v›€W|®€9€ €=elsymlinkèèÿ€W~ €W}¬../plain text.txtfsparse_fileè褀W4€W.€€B€pnamed_pipeèè¼€W|ЀW|Ðdempty_subdirèèý€`Wžë€W}Åzzfplain text.txtèè¼€`Wžë€W|q€3€B€LSddirectory_2èèí€`Wžë€W}Åslogèè¶€Whg€W ¡×fplain text 2.txtèè¼€`Wžë€W|®€9€u€=elsymlinkèèÿ€`Wžë€W}¬../plain text.txtpnamed_pipeè褀W|ЀW|Ðdempty_subdirèèí€`Wžë€W}Åzzz€®Àdar-2.7.15/src/check/Old_format/archive_09_crypto_bf_test.1.dar0000644000175000017500000012017114636066467021141 00000000000000{Ÿ¯W`ÏvTT€€ Ÿ¯W`Ïv090zN/A0b€7d „ït0¤°ÀÛu.† Ò(æ‡f^_VÍ¥€@iQI·»Ašþcý²ýK×úïàn¦ûZJÀp©k¼¨“ŠS1Ã5:C{ÙŶš"sÓe™i¤ ü,Dåe6c±Æ· Ý…TßÂɈ6þIZ¨JÆûXÐ2±=ÂèJÓbÆÐ’²°Ãæý;l,>Á8gšq ùI&GȆòž ñ7S¶Ý‡Î, È<`é$x§wŒ’›ÉÅ=ûE! åÊe&£ ë™CÜcˆ ðçK'§©…€Í„ ÐyFÉ@[6ýÖzd1# °A¿ ®5g úïmä¼ç¸5ó¾:eªQ ¼ƒ=Í–0¶Ô¯ hF|5j5l)o¤V:MÃ:œa u£§6($sœjäÑҼʥð³.TUñ#‚¨Ø¢;sjeÕþ~û%ίûÆýêEÍ´ Æ[^ˆó`º%,BÍa€€(÷µŒT_û… N¤èõ‡Ámñ·xS E;Jìç n¶–Ùn¦£I‚âxX.ÇG,? ¤íyެ¯{õc ‘$¦ 7d×HÙþ]+^ó¶õÖż=Uމ€ªŠt!˘'ËëFàÈïB÷jWm äę˼ !û“t o4K¼[tÃrê¡&Ì ¶wD‘P²iëÄKó ¹_ ÎîŸ_€“•·l§už7°¯Ç¾œà´ùpæ:È×ó¯r;©°BaV•ø6‚~`þS²G÷y”GÇh`c<µ{©¶n;ÂÕŸ$Ó˜Þ4H1– ÔaTA=Ÿ.X‹æÇ­x Jzá«<Ÿgó!v;‚Fª”PÐI¦Å¨«Á¾ø“ÐÝîGO»«sú]Φ—Gʶƈ¨‚R}ù‰ŽüK–„}õ±´ëré!ÁΛÁ]ò)d«ã®2ˆƒzxk$+MVÂuGm¼ç¶ì­Mµzú­?˜VõýÒ1!Gï—‡ÏÌárÎð!G¾GkÒ¦Lžû•ŠAW¯KÄœ Ò4RؘÞDÙ¿ÛÖ\Fßh~v1l÷¡gã¸&@]û‚€9-†;ÅÝ9;:æÇéÔ4 ¿G»­Jv´é3f¸ý½Ë}¾>‡Í5ìBìua*ŽM 2 æ,z¹½lSƒ^<‘8»ú‰Í5^YÔZì}*I½²ÔÖ¶ÿ¼³†Lýälbº5µŸ|%ªÃ”Q |Áýâèûëœg›yï£5±NUÊ噜uO8‹Þ¶ø pçá÷ý“nÈ“)w"Q4Ö2V—ÆŒº¤Å‚ Û¯H*`RËíý9f"nÙw›ÛÔ{tÎ}¬šD>Œvå;ÊÏŽÇÔ¤·%+»›ìwÏ^ø÷*}ÊI\‡ÿà}ºÞ< A[ŒçïòÄ!zŒúx?­‹=GSe…"ÿ¡Ä«ñ°‘3öØ·1^‹Õ¡R’Ÿ˜9×öã{<[2KhIG%!nA@‡uò¶ï*ÈF“^¿æ_ãGWËÆ_|ò;ß'(IöëCÃ36­jû ‰d;üÅÞP϶—r?Z$áj­ ÊM~s‰b•cŽn¾89×+¤Ñ—†… Ÿ"æam`÷ÂU³‘'ay&bÌý*’; ~®xFƒ¬B6øi½PùJ]’ü#VˆB'œæd“qŽQg,8=²‚fŠi|šÓ|Ÿ¢]; ‚¢©˜×àoѳ%B¿.?‘F£{¶ó«¼ƒ• ˜¿\[™¼õ”$ú´Ôí i&°Z9J‰”“ÑŽ8Ãà_¶¥þëw$ºÛ¾ vÙ¤p/c²´ßaO›.½ýnÅ"`R¯pšÏK1ÓçûàW9ñ—}ãYØ;NL¡¸üàx^Hý¶/¢D+¿’Ô4¶jú.Ùr£v€-°È­™ÑóáOµÎzSÅÂÔõºn†›©ÌÊšhÓ¬Á  ãh&Øì‡ÓÜ!˜mZßG á¶Rû“Žp¦dskEzùü¢ é¸D18eõ÷_o&(€4ˆÃ^í‡ •ŸC¨C¢¯Y%RÜñ뜬ÁÉ ×¼ò¤¸|9}T”ûÈßHÓ7è( ÁóOW°ayÿm4‘('‘F…O $õ^rÿ‹Ê#Ùgnbœš¬§ú2–빘pÆ Cun{¸ûÂàÍj-ík»hª5øáW}¯W`“ƒómDþަf`¶òˆ•¦Y&‹$¥ð'W$ªvÄÛ1*)… ?z±·ka/qw” Ú°¡%¢&¦ÑÞ½%4~£ßè æ¶êI~2Ézzë«[/|~¢x:m L £Žd?„©¶d5j¥^®A€˜æƒÖÈ·!Ô¦‚/s§$ÆH!qäÚ¢æoˆV{{rÜNEèÞt¦àùP´ñ°|¿›€á›¯ê‰e’ÁÁù Ÿ{WNܬB ™ðfÊî~–ƒp®†$«jç¼|ÊTL)îL"<|Oqª½Ã­ú`gmGÙl‘\¬6ÞíÃhbÙô†ÓØØ)«6|bP-úŽºˆ¸²Bi¬x‚úª"œK6+ÄŠßxwÒ˜z4dkZDUÀdF‚ŸÂÊ•¢•o -ò B¸X›è±{œCÛRD¼…);´%8ö@Ý1Õrt; kÍ/!W!Á’ÕIéùæuAõ«oª<±ƒÙ¬!éK²wî ¾ÿj£‰dï÷qã¡7:ZYØÈd‡¦e4y´˜õ¯8×vß×.ýŸÿ¡´Aƒ¼M‰GeeHJ»Œ?u–ñÆ·+•]º{ÒëÄ’ì 爺öГŠêÉ›e>›¹ÔºP}žÝ<í«à4J€)ÖLNŸ9²è¿e²:þÆ(®Èpa†^¸9°ÿºi4C§ü/’‚‰eÌü‘ìpi²´qÄrÅÍ’C;hî°Õ@l¼¡Á•¿°âA@‡!|ÔqzA™œG©‰F,Kj Mô{–-L=¬»Ø'IDžüP¸åyבb{eG§›øbíOáÜ/äô!0Wßßžö †þi8¹ju{3 Ðá[vH´b ÀøÇ1LrYãÃabÂfëÝ X¿Ÿ5›®{“‚ˆE2‰gbg|‹˜ÂmQ¦ÁnÞ4ËS®æj?·éG€Œ«í»@¤¯=aÕßËdbšé©Q£{]˜b”oöÏû° @kù+å{׆HÏFnä#ÇòMM}Ȳóþ­ï§ö/™æz±©råŠ )(©â.å—&P·²çï;V¿C;%²Ù†žö°ú9¿oÓÀº ~ð¯+©”€¯G1‚ÁLnn¤.Öš UÏÇÚˆ ¥už³ ÿq\sÒWvwo/øÜ®¶åFùm¿LKYÒ‰u»Å›™ @‡#Ë<òi*(Õ1?¦èè4ùÍ“Ž¾oâ•°•! VãÇæ2Ílç”ÿ‘Nûì†vÓ’®paÜZœCgFÞ\ èÛꮯÐh¯dóÝT9BïåüYäœ`Üríy ˜êÔÝûJf0óQö»xô+j&õ\péæ)%!YuµRãÄ|Cr8HUs½ú[ÿqjÿ\ß9ª‰¤sÚ`i ¥À¨ËIüŽÐ!ÀZ`poy`†ÙŸgìRh„ì­*Tî1OÌ0òÍq§…7=UáüÆá`Üœ¬kÃîÕqˆÀî\EÛWº`ò=ÿZl5KèuVinŒÖ1ñnGe–ß×=f*çO¼‰±¯íÃDàS›ÉÈ^ŽæÙ×Чhr¶x¶Ü!Tþc‡È”!£=KŠ@ψ²á ¥œƒˆ‡žr4"2BûG£ìΡB„ ßFŽÖ R Tøã—&çô÷C¬’oÓÜýGÆÎ)+×_-¸LáóqëÒ°v¥‹t*‡Ê.¼p5ÒÌ0¶Ì3R¹6Ä¡eÀsvM-– dg^0ò9°ê­žšøø½ ““B¹MY¼vÑà[ìïÝ|?½sÁã<ÿ¾w)(šán8ٻơõ§}qß0&JY”+§àhDäbhC}gᎠfå [ÈhðgAÝ CV?Îú‹­®½k¢ÇÕ¶ˆ1Z…IÞs¯¾ír¤ÖÜDèG]³s\Þ¡åãCý«å° é8.ÜÇùõCÃAOBOR¶è“«r¯H£G€Z$)$ Vc53€I­àÙ²íÚ—}Ф‡p‚:HÄ`LO µþcJv :ÖôaGó4n>å—±ºÏ’”Áîª^"RyA®Pªè6ŸKg(<ÕØpý•{§Â>ó9D»Ù+¢£Ë_ú<”ƒ¸ó¨((±ÀÕCŒöÃ)dÜßSïž'!;"ð {îŸ%ÿˆð3p¬|ü:~0¶&À³¾–~„Ïjaá·V郲i3,zK;õF™fKWº> iù/ò4çþø7°ÖRnæÈ<©8—ØL“Fãy«14ÞÒÆ˜—¥&”èÝJŒ¬ÚÖ9¬@^ݪ»áW´ØNÙ~Ê/%Æ%lïùÕ~âÿ§¤[/©R€f“³]~ä"E; « Ó€†­ûÑÇ}Õf6üÔ™Ýp©þª@¶Um›—ÊΪ±±òYiÐ8¶*6%É£“÷JæŒ9Iã øÔÃi ™bþÉá O¼@Ï[ÒÍéËFâ¥K… ÷Uæ‡t©éÔD£ŒQÀ,§ë´„¤Ì?Ò(ÖC¢sÏ©2yéÌçBl dŽüæ¥E ¤fY• øåw')«ßUÈÏF/wßU㙀µm«d8 â«rÁ¯oF-L(šÌ€žEYªžµÌ,´Çju'aBý@̯óÝ™¤Iq‡V¥²Àô•Â^Bß$'¹#©zÇQG¦o¢d‘…Ê·çvS3¶Ñ‹1§ÆÕßÈ4ûàÉtŒA—×)=Åá,eLTV­t8y¢QÆ‘+êi]|oT<3ÒòIcá Gˆf„Ç»oT݉®¿ÙYþm¤ò–=ùÐX}Š”J`žÕËd{®v™"7£¬'¹×®aœ0ÇA‰jö¬«4× Â1½!OfkãÀÀµôz|OâN›Ð‘ Q‹/5Kê¸éÓkóõ€h üþœ¶ùÓ~>⊾ñƒø‰Ížr"Àõÿ‘šl7.R‡u\·Z4)‚é‘+4‘ˆÀÁAÄóe`# ž„ €,Lc™\à ÌwÒOt§  ʈmWŠcʆÁ§ªL2ø?)…ËŠ.{^9Ûµ•Rϼf¯\øÌ®hk0‡ɲa­*‚&êþKS¬MØ–a EIu€oý?lj®‘ì6«ƒa}GŽÒè6þµ7—‡É²¦h ´¦Õžk¢hÖ¬bÎ6z/ôöö}|³µŒÍ·½Š MxùÒò¯žK뎇îÚªš¦€I‡ý¨pCõwý?zÒ* _TyF5XB!-¶-³~ÿ5ȺÑ9\Å܆¬¤C­ ±o¾þ‰6|´›>eÓ¹q,’3缯Z.鹌W=6A³ÄïòLžç&”úOÈ#+Z§^ºÃ2ÁÂ`•žsiòÒ„Àä,gêÀpçV‘Ô ­ºËE7á}8Ôtá–Si˦µo€žF™ñvå[)a借d1®Á­[Öãny“üŠÖ" ÷öUŽ¿duÿ‰ŠÔaÐÔ­)[iÂY‡ÒNù®l¬œ‡æ@Sö“ /®&ïieè[ÔX‡¹ßÄf]e™&í3äó)cÄï WlœEz> ýÿe3ãêÐ/1ü‰+ºl…ŠçL›ßQœkŸËtrCöâÊ„zÓ¨¤ö˜oôM0'Çs[®§ÙÏ•Kägà=vU®*Eˆ*>M1ŽÕÂ…°‘T>Çi׎]åVvYq1ŒÉq1#6~\Êiyöæè8&ÛŒqÀØNQ£¢ì<ës¿”äW•ãw ç¢uB™ 0 «T<Æ,> @ÁÈÔ­èHè;¿u- : }daZKD“ xÇËLP/OnŒ#}]^›Ô”-!z~—LÖ‘?lÿ`càžå.¼þ™ÎMO^Œ¸ u„uŽQx˜gNä(•ò—Uo1o:þ42¢’xüEËœd‘ÿˆæ'éÙïH™7Χøûí`u¢SÂ"µ° _Qã)Ûdê¢NÀ6#QÎW½E6Fiš [Í¿¥WHùvž×ïº×(Ù¤´¯NEcšðê×,§s˜Š’.RÞªNÝxó? ‰h«2žµð|Q¦¤nV’¼ÞˆãáÄ6> ˨Ð9:,9ô— ‡òÁ‘xË»ñs„ Š(6ÿÌxCwÁå|è4ÿ.l´*ËiÑe ëwN/ N#9þX6¸fwJA嫎gT]ïÔa I&Ø¿e†îÖÁâà—éÓRI¸ÖÕLëgÿÕzåq•Ví/ž`PJÐåÅõ³ëÚ|lèT¿n,ðÂA%LT7 r8¶]G³}Nq,ĬŸ¹œ18{†j}Ý5"U5Ro¯ëX ­–ÉsÔiÆÃ µ•–£{¥Ç§Õ‘I`â*ÍŠlîôsçœ,Îä20QàKñM™ÐEh†]/¯‘úTúœAîÜêá¾RGŠž[­¤ËçY…osá†ÂÉæ7ªHÅDp¤5××Ô¨Ñݸù#âè[tŒbSу‹AlÏL7‹J0õ;K»ƒöä +IÔ{á2°x1Pî×ÿ´¨;¸Ü`¹ºË (hR«® Á]˜=0t+7 p)¶Êð2EÒ©\±/js2žÍçi‘† sÖ<ä30ÔÁƒ'ëìïf ŸÃ:Oåf-÷)ƒA߲̓*Çqú=¸ ¬ê’eÏ7NÐú•KiûÍ~V¸ÿl¤·Â²Xãè…íÌä9ƒ¯ÙÖœa‘Ù‘P*×m"sâx6?lI”wŒ äFy0óÑçÏV ^tSµ¡Ð'ÎZë §ÙÊ´2ìLšxt€,þ Ñ 6ÆÅ3)él'ͽbY9áíû1Üx»>ÆÞ2­N™Ó1º7ƒhªÃ‹&¶oêlŽ \4©=;çË#Ü[ ÑÔY'0ÐaþÿºôóÙ¥·»laº dªFåäšQò,*u{îù("ëp–-.  ™Ã/–ë`¥R£‚þEÍ}n‡C7žnÓXÝxÍñ?hK»ß.‰Œæÿ Óë#ÖêVnm×l¦‹HnÈÖÆM 4ÌÊŽ¸1¾Að+:ôN™0Í_껫nC©¿fÎñ Ä‚}6°Öø.%!ÿ€î“‘D3\Iª Z#Ý«'©í"_ñ-L¶¢S·œ'v#)AÅ¥qÉyMר˜Ô‰¨ø‡¤|s‡ àN‡ybÐ¥Q˜ {Üâxy{ë§‚µÉhj‘í˜Ýœ˜Mpšè–ú†îýbâo¹’ý`‰ ^ú+/Ó.”®Õ‘Éq®K»¤a€®×–\V­Ot7\—z5&*IvÈ'/?³¼ ÃífqX6ì›áZµÔâdŠGøó{ 48j¬Ü»Àî7/¬k}HôŒg8¸5çCÕ‡7råÔv™ië3›½Ð‡T1Äy䆯áèn‹¦úÁ)Âð„Ú—ô)Q ”ÚOÄ{ðª`Ü®Ÿc´'CVŒàSÌh³ "tUâ°\¤Oôu™pBä—èL†CÝç-Ô@…;ürýŽgøT#Nà%²T‡Õ“yßêI ò¨¥y¹Û7gX dÒ—™súLK|¨. %®[þc:W_½7;¬zÎÍ(£ä|wr÷“Þ!¦kŸVe—«Iâ{É™W¤7¥Vîý’y¬Äx’@¥(/õÅY®ÇEvnüdvÍð©©16éÇ=Ú:aÔK9Ý{ _x%¿Ê#1+XSs™¢÷¹Eo»ü \sÎ+פd-·¤ærü¤œgmÀ¢êõ@ ߑ˗Kàj»éšVU©2ó@)D`5<²¬ÀöÝŠÎ/1±’‡Ùs×Ê`•—kWâ.žšê € «ù‚p׿°R4|!ƒêÇÞ¦ïHÈ^L€D)Åøi°;°Ã… VT#[P¤›I…ŽL µ ”‡k­€gðúû·éNDåaWAGç8àÐáîŸüʦ…_%ý®ƒÖ¾Ë÷YûÎnkdlþF×÷L2°8#7%.|Dg都bò©ß‘>…K,…³ð3ªôœÉ9‹·ŠñåyX±h ¶*Ì5[íÑÁöË„CL2CÞ) $D¬¼&cŒÑ ¹æ,¤¾›àÕSHSœEZè9–”àÿL¿ÓûêÌ“©K¢:søõJ¼Ø]þlöúX^¹ÓøU$X¶Ž!¶ce¬FÒ¦êò6 žs†e3æäÄÁz+Ôô²6¬ÌBç‰w ýó%x®…`-dÖ¯EaŽ l9»‰çІCSèYMqÃxÈý®.F{¼Foyž²n€Ép=öâÁ×ÕØÛˆkv ô#°b|nï$1`æ£ :ðöb¨ví(C~t/]˜p±È¸í>³$¶Æü¾*{û==ð„NBš;jÛÐ,CV9H€L.ã:zŠä e7ï ‚‰nv†¤©á(ç¸í';^6¨_IduñyST/!~…ŽÐôÂçõßħãÙÔìŒAãQ¼k9?Èh²Õ™<­h.Bz³’0M ©#›•â•U=(/il˜ŸJ H1ô¸æ>.¦ÖN_®Ú“½ã²}áBî åìg•耞R˘öƒzt³ƒÍ­+}òÍlç”un-˜ÉA£í¬ØW`6|£÷Xkå–kÏlw:CÿÓ˜gTÍŸøÞëP$Óº\6p^=_¿]E´QÑP9ãÊŒ4vîE¾¢4¶ÙqaÁ…æ„[Jj“"…8çë!Ü?wóë%4Þ6 Kjh­…» ¡~f§¿Oæ©ä× eÃakÍ2qÞH0S£ý7êb8ÓJŠ,?–¯µ’Z¨Ãi /¼º©µÐÓÐß²!¶æÛ®ãß&uÃ;̘‹‚ý:4»%ÜÒàñÃkŽãz / î;Ð2&ÙØ<²ïA~åbjþíÓïÇþæÚÑRˆO™hƒ¨µØ†ºsI ¸,!ñ=—U,NÕ–gh×/ ¬(3ìŒ-é'1yŠÚNtr`S@NÂb7øoa$>RÅHêEÓ…ïK›¡J­[–ȫ¸ÃE/õz#i™ªµ§o%H„æÔÈJcó —E(Ê?1ŽdÃÃeŒdª.Rm¿O¬#a¨ædUS¸hGì߬{þ/ž1%Wuñ÷Š_ HRSú8á ŠQ è @ !IÜ;lžk‹c¼ª¯/A"¹%Ñ¿€ï8Q_övÑ¢ÿmßÓ„ì×b,*ˆLŽ·F?¸¬çr':4ÏçÔè|¥o“*‘ ëß!œ;v ܉IÛh¼ã·‹ÊŒíÅÿ÷ˆ‰ÌÏÈÛÉ  Á(HØ7`(uõûû'˜´0GKéîÈ5…Âõ8É#s{÷ˆòæ@œ]rÝnûÕ±3'Ävs .È”·Z"“G Ö ÕDÂLÍðh7ˆŒWGMa#±IDãq¾yàF­wûR>ÂSmïžÏÃâ¶ô/|q×!ê~€nBþÕ<£šh¡]Å»´ŽAbñœî”óaW§–® ÈMð˜ø]€Þw¥½“Õâ 0Z¹þ5–gë·˜vg›æ¾í/S¥Ö88|3Bìˆ"e—†«W¾F²¹!É È\^ÈNy›®¬Ý1áxÒ¾‚7üÃ`4qΫî‡Î÷¥ü€ ƒdï8×Ã×¶ÇZI ”×ю~EôæÈÉPÓ{Õã¼½ºÕá¶iÀ¥¡pi—´’ ðšlÿ|Óêéëh°BÙ–e|A{§~š<‡•µ—Lïò7Ôš¼¢ c¶ÛÎX²5õòSØ]×Ázh0g–2èÅðáæ~ý [ÖÒ)Ï)ÚëB"–p=’¯¥B BaÄ>/án*éUÕø18üG½ÖV Ë³AûйÆpCW—¹¸JL!0\1 •÷SÍVË<~Mª<)–+¹Ý:@“èèa'F+âvÀãr³üVsHEV*úMÈšƒŒ×WÖäã³vNvàÀsiłדÜ[dñ½m8¦OΖ4kIÿT_… =E/ÐÚX÷ôßО—žU¯+mU°¤ ~kL)!é`¨¥s/¸MhS®ÁÈ^=¹Ì¥/:y^a65&öêeB‰"_³¯ˆÁ"ïéY–?… ¿Eãé!´IÃJ.,"¹Gµ›†ùà÷oŸþ®U’nˆ#ÊpV›»o ¶.̓韱t (%S+©7‡úOzòêfïñËWCQÝpº§„ÙfkµZÈ—»°—§}êe<l_8$w ¾ %Éä¼U#ëÞ¢Â!ôr¾H€iSŸötõ´×\˜Ã Ñ–-Æ^u)ñ]XË"k AÙ¹‘x׬JS¾žá“éìþ)"“3‰¥þÜžE æ{±gýðÄ‚xyj‘. VQ=(gôÍúâþ`¨æµÊ0†®¼;þ'ìÈâP³Ùe¾€¸ã¢b_ µ«±1B-Iô?¥…Y0j¦‚ã_|@ ¬;ʺд‰j°®ìOřӱ#±ÔæþáôåGünö³ö¨i±ªÒ,Á0Ô !ƒ¬!¤?ðYÙŠB#à¼= fsßó²B'* é[Ø…ŠÛÇDûdðóŽ|'ŒÕ‡7à‘G] M$c ³µQ¤U–¨°Üž–Ý(v0tvä¸ðm7•YT~³’¦ZÐóåÝáT§OgD+àó Èí5·~/ð‡m EÊÆKÉ™ïøðŒI‹ò¨eAÿ¡m*¢Ævי¢·I™VÖ®’¸‹7±_óÑî䆼€JÙ8 §ºÄ!]ûš 'ÊšˆFcÀ‚¤C› Ðæ(ŸHcÔÞP£ë¡­ÄëÄäGM.Ù³™\bœkc¬Ø• *ÚqOõGó-´”]§¤³ß˜99 xÀ>qò8¡]Âgƒ“a$™Ta¡ð²ý›¯^ú-ÿêˆ3ï2xp3¯bÞŽ‰Uìgk€½€A¿ÿHXEOC¦ã¹;h‰R•‚»2—,£Ø»nÞq=áp'nmšåOc™ð:¸ã¹TWŸÁb¬…Pu’¤ rýŠ ×n[iCæÉf~¿È ]Ãf½H:ç[Û;.º¹y|·Y"°öÞYšŽÚLš5âÜcÖ?Àó¡%AµàÚ¾´”ö?j¿(Ò°„sD|#¸«rËØñèö?Û)ó¾ÿ¤ŠÍob¨u ®C_MäS_ÜáˆÖŸ ‘v[…éS®Ü_t´^f0§Èmx¶¡Ûƒ,šñ˜äJÀwi‚Ñã`"ÂÅxÅè©}°!lµ–9x’dÕ¾G@˜O( bS„ _“F\,ŠuG˜b² XLÕ%Q‘ÑìífŠ c”ÜØ—ÎâWï³'ùM¦¥¼<~à½Â'ïñkK÷§pQH륭B­€k¿€¼»1«…­:šý³*÷ôß E åíQᦩu1ÇÌ:î¢"¡æB‰Ak’†&Üút ¡·` ®ßßã7QýÕ‡†²bæޝX(°ø–`#–h¢‡#DÛ‰TN‡ÊVÄ›×™rz{j'"ûñ< ¶bKULE–ÀÓ–Zw{<ó.m‘Ö’Ê%óçYüD‚ÒÊ·¸•õGya¸7LʵH§‘ .K6þ$Æ©]¿ éÑÏgmœ 6j#Wî÷ľµÄm^뜺à[ô÷s(>âõ?ÃzÿŒ€ïçÐÀ‘åï¯ý¶+#»9J° M7!ž%¾C‡†Ø×Ó¢kíÇØQ…ó Ó mÒ͉0¾2Ô^ö $V£+žs¦ˆiWë\5:ÕŠê}`“ª¥Ù'ôçàT—ál’BE/ƒçðõ3Á§àÜðc:^ƒq8=i­;ôÀA?^' Voºo„‹æÁFºw›ýïû¿à¸å›*›ó¸”dˆ“ŸÒ`bS ÇQ!ü*3uÅt–ðø{O8,¢¯¹a9œ#PõfÝßš¿›èðü>B.èT9ó¾ºjf Óìv™n”½/å²ì¶ê0 %’ Jåäò nOán³KV„–‘ÈO¦ìGc¤ÜãqöOǧ&UUúq=Æ(?õnRm™h5¨~ÛaîÜôëyÞÖËÙÀ Þóÿ\½*ãeäñiñv€#†Ö[Œäö­M»¹©±UÓ Ùáù0·èZn—œ…dydrT‡±Ÿ{NJ^ï>G mŽ9l!1«©¥¥5ˆy‚‡Œ³„Ø'««]­ŠAûa‚ºö…ðŒZín–ûâ‡jSßÛËZ~l$ÿŠUt£ÈË2íKë¾]d|2Š|ŽŸwNéÚ}C‰¨Ùàó–êÌkë4§^ªd¦² ÏC+ŒMó‘ÃΖÝél¦XppO5¦olSF*»Pp ºånrvŒÀFCÕötmp^‹âd‰p8PÒ9Ê?뭿㑼TÄ´¿˜p„™ÞKºvr9~dl#šá*ð Û@ßXV(÷'‹&×fJý÷¿ÁʘÇxš6`Vy©’þÄDÓߨÛ<\!ƒ¿ž:ÍIy53[?ÊáÁ|•åë>Èóv{¬0~z—¢E~–•„Ò ÕªýK{ ´Ô;).\¿ï$bBÚˆðÄ(]…´^‰ª¬+ð:Ó+^"-àžÿ‡Óžkõˆ5r•ÒVb¢“†Ã ü›¹_´LXG'1~·n<äSf#.NòŽŽk¿BÙÌ †JT~VÎvضþxüÏÂë­al×Úãƒn§Jâm±ë“ðÏÜDzî|’ÙBçÄt"«3Æ„6Œ·ú4ö RãÌvØI6ë˜o0iÄ*s§¸s¬Öò(¡cÆáŸmƒmIÆ2»OÖL± ³6†–¾z…xxp ѽ~µ5OÑÆ]•û¶A_E–ï®ÈO€0é”=X˜p‡êÜê¦Ã@ç>ðéã”2<ÒÏÄÇkb¿H¹m¿+Äœ-ÍøÕø!J‡°¢¸=¯óYõà×jȾ^ÓfH àˆè±«œ·¤"g1¨þ6³9_Ö´± ¤^ 8î¶>zòÍ+È 9 kâ&L6ûŒ®XJˆÃÏPÐnl‡Ç/ K pG0/ ô@г{çpÇ|Œã¯ï’HAÍY MF¶ªj°™U Ÿ–x¬öWv3ª Š «àvêò{?,héTÅHD¤SΟ•)W?ç^ý›TpÉ©“¯‹Ã !Šö‡úv÷ây.M$%XÓn¨‚’ÅΚ#Ò€µÇ7j6®ð8SWI #`‚ Sâ.‘é€86}¹Sííe•1„=;øÛQ^m2ÓEï/»Ãº)š 2ÛjÔÜqéL­ƒÄÄgÎu\]gqsÕ²³ùžáð#Å,¬J1â.ß›J?U§¸» ,bá±®¼ZŒyÉoƒYqÅÆ:QÕ½p®U0_‚~x[=GêsÏÊl˜§Äm…#U?aüvB§ÿ –ìÎuÜ&£Vˆ¡0òz—$WKw¾™ÑÀöo\î‡_~1AÔ· ©¨JÏÁ4·ÃLkûðâaã5"tÔÞ‹ŠšÄF†êáðW”[m¥f" ;<J-×)ìÌÕ„ûn%’äБN½L™‘°‹Ó7ðL\¤©ñ®L”@|³¢r: ÞFwÒ˜_ ðªùŸF)ú7õ|¬?I°Z;eS¶³º¼Q›Ùhü±qw~­\Ñ_š6Ï×í0_±ûûÓÏä í£ýȦÓ°Ãrú`êÖ Òª‡'÷Æœ¸SÐïb6_ÎçvϼWé'1T 9ñe›;E~ú[šbÁ3öN޲£ -( @‡ë!?®©×¦¨Rëw0b·ü„ó£ hñŒLžÍ¼»Ìê]FãíxÐFÀ•ꎭàú5äì–kKhHöß¡©v½¼ÁîØ¾uÕRz¯¾/;®ö›oï•Es°×%2‡1“B]¿:­¹‰Ô¿«~ê4ÑY ”RÒüI‰TËrQ{©–‡ÇëFìñzœ† e#wÓ66Ûf¦~RQ&øƒÉ¡ê 6(šwsÚ¹žT˜Jvœ¡ú \®—[5`&c³ô ÆZŽ)âÓ™´þÍ uµûeR½éí¶Ìc~!E«Ù9;&R—ÌšÚmîž·½¿Ò†Ñà7™ÈÂe?Â>¸W΀ Û.V&TäðzFîMˆ’ׂۺ.¦Ô˜”‹ÞÞÓݤ\DaY.“ÛY‘~6WÏ-”¤táaÚßNÌY{`QdçikZ Ë̲ÞéQaS³©ÊÒ„Í® …—…ë+|½,½“pq°òëA+ ¼›R–~9ͰR³¤8`|yË3€gŠÓÇKÔØ8/áÕµýˆ¹Õ’ `Ü!qzçºÉ‰F(%UÙÃðÄÚdÿLàÃÒ€L¹’“óúÚæ-4ÇRå OÃX¢j‚ÕùÜ·¥FC×#¹çþî>‰úªÂ9ˆí¸Š›ç°Ž„pœáq—Î RɰO…Õ4ª˜qPCî œ`ô³ï–ˆ6ع«|øiÀŒ%;@õ9?B÷–ÐÞzø 2¹W@õøÈí.¦ú› aÕ§þO%ã™·ùÉg¨«»"¥PÄÇò‰YŒ+ÕwˆD†Î Mš”Ú¥¥qär¼n ²ËQØ„ÖÁaú1nò&# ´åjƒuµÜºŸeÀׄ¥¨|jÂ;â‚ûèm°8A¦kWQïX¹M?E¢Æc¨ŒÚ¥$#§ÉqHbzÇGC’²_YáðŽ}p¿mD'+ج­ãñ=Ç(ÚáQùe1a ãÔGMʸgO¼.3¢­™¤\$¥ažjŸã-ä7ÿ0ë~lÈZ,be:Fûv¼—}.ï¬gÁŽ—|G°UËxf0"å¶€ËâRÞö éf`3´ÞEZC6÷NõCÝdg™s³D8t- _(\©Öñ@Äúy±uù4UÀ†ÖÀ8:âPÕ½¾¸ŒnÙ&vqêøf~êáÑâ†~ë{ÉÂ'¥¨=EéK.ªmp]‡Öè³ s­)pÿ‘™øç½Ò¯Ÿ? …†>ICÖe’üZŸP Ð%‚›¸ °Q’]9ñpïÃ¥èŸÌô…@mþÖÕÉ ‰Ó PùÔ¥L8ªs{Œ05nÃ| ·CXh`ö›Í%TlׇՎÜ-kœnZ.Ìy{Æ!‹Ç"ïšlÁ„"Á´KÉg²UT6Ä"LìÅþ]ª"ÍjÁçY,x/½ÇS:“n\4\:ø§ïŸAtä–¨€’î-‡Œ•¶hòwu£JÈkû6›“n<@³F“ÂI˜¬ŽsHm AÔ]?Mb² A÷^|Ö!ÌÌÛìÿvë„ýýÐÁ-ÌiOÙÌx1ëøåì©}€°–0걿²ï‚U„ ‚±>7¤ÞÜw}›z=‘òÒÉXœÂ›§t)5‚EN¦ä”ZÐÌ-NW_zk7¾æPLݶÏw;VЧn]„hÿÇ¢.Mé)¡…†õͤgh%%ŽŽÉrS†Û““Ø®å7΋Leë­&Ù|VjÙøƒu¯Â^1¬Šwõ €2óX“ÆcüHl³Ó ?¡8h[ƒ®<'¡7]K¤÷ïèlŠgàRN6jx†˜‡TÒÕ}öb¨_…l‹C ÛQ&uˆS¿§‚Lú•ÄI/í¿ó^±.ååM£a¯$.ÉšZ”};ýÊIKÿ@§ƒ‘ç†Ó.Œ«õ" 9w]d*÷'ôk¨­%dVß!)ÒWÔ¤ ÉÔó•¿'‡ý£¡öýŒ15 –®õ¶ž] ôÄ{#lûGóHÀü‰ÂšáŸLŠš²>G.n6óøþC‚à¦ubMX y‚=ë–üËÁc¬Ü)ÛÂÑ2eAù6ËJ„ Gt攽»ždcmh´Õæ§¶i×8âØÌ8ÛÑ¥Pêæb¶=¤äê;Rá\é¡·qÓ‰¹ï1­C•'ÌF:¸»;­<‚_¨Œ’ÓÑ]?§¢¡ºŠO+DØ‚üÉ{þ€®ßð¡ú$&;ó•¦o`.uÈ^×ýYͱ»zhPpµÔK9Ǖ̽§Ê¦Pǰ äâúîx†ŒcÙ­_½t¦¡MÔ²Ü;”€S-!f,‹|ÝùŸ‡úPƒ¡ÅpòózÏ"ÊTçÖ@^*ë¼»>üœÕEݧz'Çã­À,ê–yâ‹Ð58pR¸x§TÁó9mÃhIDèJòcZî~yu| ¾«Áþ‚éãÚÓ¤P¬<|ùâ~xûx •õœó§5÷^eÆ ½¢ÒÔVôpúà=FZ ‹±ŠÄQƒïNrº-~ž-¨Umƒ¾‹àÒ4ÖÔjá5»÷ø»ON›~õ@š¬¸Šušž-Š? w–©H™3ÚùÂŒ,ÝÔùèw–ºî¥ƒÑ@KzU¤ÝT"Èb½S•0njz—utݦîù>Т6kب¤GTĩە-ù4aÖsIU9B÷E£9>£Œ‘ŸÆPÄxa—v~ÈwÀ˜äPŽÔ6uTà (ßßY\ ·wZö_¬P»hKŸÀMhünþã}Ñ#ß+W„@6±W„ý¢ if¥ËïNoaå0YËéxø©»˜S6þ‡ˆ³Ó®·¯j hkS-Jž˜gTj,Fc·¸ÂÏS…6BK(óÅ—úÅÀ„ªv-í“H‰ä^yPúfËׇ+_L>VqÐôóÛ)ÍÍ/@Ö SÌI&ò?ŒŠ°l$¥¦n•‚¡èóÙL‚gQXÔ¢Ú3I:EKeq¶bª|5ØdïþŒÑ^P]»º;œeô¶*ÎѰ–ÛÜ9ë[n=r7p˜à ê[gÜxÐ̓.×ïš „˜5ÁXÃ5Ü2ÂØEâ8 ôp¡FƒÂ¶ìüUÿViþ‘iŸÍeß‚™ëF§å¹¨ñB‘X†&]Ëþ…ö>7¶ù¦^6koxëøŠ«TámÿŒÓ²äµhº›Y Þ:W†ç|Qš¢uú“aÓâõ”´øò|Và#F9b\¥ÖÙAV8ÑÎ}2¬/lR¸½8üL U{8q1:´ÁÒÒO q%¿@&$ôçš5èJêç òGYÂj@Á%$ê:Â’YeM€æ<úTÁ˜Þ¹ÒIV Sö…L@Ï(ÅÀ[à$Z²_»¥mD¡Zé•:7¡¶j©Ê*9þ¼Mï› n±Hkd9 ØÙñeüÕGÂÞ êV$âpêR´ÕzŒç&áÏphJo´EdÄ-š( 57PØÎØR(‚"4(ÓPžý¸ŽÏ[¼GZÖïÀ ”°uFÇ&ðgTökª ìÄÍü& M‡÷B|QN\9þ-¸C›Œ˜Þ¡-ò°ÅÀfËOÈÁŒs} #Ó81Ž d2Ô„O¡FæŽÃ‚óUVÓ˜†€GûŠõâ zzK×{h+ëλ^ùýÛŒ©yËÀ,ÐÕü=ÔÍX(eøoþð~:ޤd³QÞo.AÝ4:ØkCÑM £Îç’#äD´­µIŒmå.üZ³ýÇÒ‘ (mÝ×e¿¬ÜšloÒIT>ŠÈÔ•k¢luí¶E`³zWÁÓÆI€fV.l6©6PÖ‰}óÅx¤! )t«Q ;ç„ib•ŸGêù^AôÂá©ee£ Wˆža̱뗊uÍm8 ýÈGÁZÔ=ºdlrªúœPö|®_Õ`‚›¤Î0¿n†€(¯ï\C0G–gŽ8'jKâ^ÚœUʆhôΫ¤ƒà“z¸ÖÅböm•s%è,ʪ@8…Åê¥i¥) ]¦~º—¶¤KP‘º) B͋ݑÕP:KTÓWPŒç«V&ÎÙ­~ êÖÒã;NäÝ_±1%±èx™~}«Å5yø&¢¨ÆÏöõТ܍[Ì[ùº– ¶Á/‚l¿ÝösÔ¼²Þr½ô­˜J 9pRù»ê×eövóN"7ÐVEn©d#¬?è°°¦º¤ï|1<«ß$žÔ»ò³[æ)+´IÖZáR<¦ ºÓK¡CÛ/Ê¢f°N¼N¢;Ï 7i“«Ûþ†kÜÞ‡yòM}Öœ/u %/é–½òë'Ę-ú¥ÔÙtÔ‰W9mã*A]Ų̈(ëFÌíã;„¢·ŒóxvŸ+ÚþÙ Š5O§ôX†IDë0®%€±@1kÁ•qF¼–§Y)CûâÕAžVÉÔuüÅ3þ¬% ¨³æÒ/ª[œ®%(u2#Ú¸­A4ÏŽäv¹ÒT^T¬ëI`Kl©†˜oT2û¿X"} û+K³öšFÇåoÇ…µsóEÜ£5´8Aõº vOÝ ) ÿ~Âa¹¦GXœ0Ô¦¦Zú! .²u4&rº`ŸX l2ØPâOá/=Ò‡ûÇXsáNyÉW1üV–x’ÑLá bmÝÀÖÆ#Úܹ;Çaû\?7Šô/›¾ëÙÀEOÏwrÖN1Ö¥ ?†bRð•V¼¥¤µáuíÙÞâ@iËõPÙÊ÷L 8©Ì:Ùš.¶ç³ÀO>±ŒÔasŒ>kÊ!qmN†1߸SvèäÓ`Hãá4 Š* Ý6Ußðê ÊoùÑÚqбòð @¯p­ÈÙ†À[I”S|å…Ÿºö€nÒ»É$Ûø`MTÔÆÔÇhèT‚ýhr€–1½òf¿‹5ƒÞ´©×:R ¬ßva¥Œ0dZJÓ‹Ì/ˆ&ñ.Æ-ÁÈB‘8êpažyj fll[ú`‡¶­„ñŸÄð²Þû¨£ýMå¾Å“A {h@¢]Q;ÎÑ߬¡®£%sàÒ…ïjªˆòŒŠ^ûß@D² ƒáVκ$MŸÓ(Ê…š:/ž¨Ç¸˜U!ŸdéÿÓRIºãý¡f< æ¶ûÏ‚©ø¤E~ò-5s8 1¯ˆ{ì‚4²\ÿí±9°!æŸ9Ú|D·€rb6âqT´Tî¾Ù\m÷¡&‡ sÎ…þÑ~Ç@謳VLBú>\†<¾kæcøš£–Ÿ¤ÉíÒaùÄŸþ g^‚‚}“„mUíÚ%…±˜µˆ 彌å4UWôÅó»DYÅ> Ž%ˆrœ¨ü)Qr‹RÎ*vw›½%–z–V‰ì5ÿùöè ŸÃ¿Íw#Ùò²ðÈÆàÇpûÐò¢g÷Sß±K2£Í {ëx8«”/.õx¾µU(—ŒD0vk‘Æ $iú§üÁÞID®ì—X tÞZZkÂÅ÷ƒJ×—ôl«m…œÆjƩݔÆå°Ýà¾CGAY~_ 4´*—Šðv͘víÒƒý> ü¤âL¿{¢ð!ó>• /×E†ÝD¶V´†qg¿Ô³G{2à%î¼YÊÒǾ‡âûQ¾±7¤Ü>~ÐÂïO$”E!]áa<žÀ/ÊÚ‘À²úÔÑ^R×:,jÖ“´€ÍÒ¦u/ µ¹"6A‹’â<ã—៘íùÑÌ{²O‚.š|-‰ä»sæO¢¬’,ÿ¿b²Æµ¯༥-¿³Â¹< ùÐö…Ïuº6»T‡Â7Shr)"''Šís¦õPslÁ Ež±“§òpT]_dy™=m’Ö,{a‹SQÑS÷yO&´ß ç‡ØD)²KÉ(W„÷Zãa)¹?"M·†j_$ær€r¦:~gÍžÊë¼y U¼ çš3O_dgJ¾h–òåâÉDǵñT@ÓTÓ.ðê^ër3¬1ó…éöI¸rZ1)à¢O•žÖr0ô3„Z$=8#uÜãl|‹ìI·ÐCSÔð6ÎĨ×!‚bŽºf]‹e1ˆÜµP~:=“çNáGÚT|²Ž«YžÜ¼quñ,`a€ê˜q#âÃY'åªÕ Çõx¡ÂÒ­¤Ýê²Ü?f®jÅÈÖï½ÄT.êß7ŒÏ0æÌ>Ýžó=Å%=ñ¨ÌíAE W˜ øæ‚ A74üqzÅðfÖOºB^ÿa£ÖÐ\È<¹ySÄA¿Š«g±³åSwL¬[²RqŒ)9fs©º5iÂj¿{iÚÊŒføpxüެI£»)ƒì›£Í¡µh;Ï6FXÍ_ˆŽàDü ˜3ð]å|]¦óŸNç¾1Wº¹ú—­¯Ãuœ3+@Ùùš9_¬-=æ75ѵS“M7>\YÎÜapñÍjŽ€~p\—™Â6ÂNa3:rx9¯lÐH–‰ßu“OÀvL?³uÛÖãs"díxµ*§ \•ƒjdܺ ¡³6[@•ÑV!aO€í[˜sV¡ºè‚4׃©/šáøÏ{¦YûŠÌmR_þQ×!׆Ÿ§Tê⺒«).@yŽqÞ©K‚¨v¬õý Ê-­Q¨ˆŠ„®/ßóÁ¦¯XB\gRÈž3–'*¶`‡®>CWÃêÈꟜM±š:»ˆÛ%áî”~{DÚ¡Ï~D"JßΟ{‡—Rd*dg;×¾ëÃSp wÌD5…Ë”Æ`Té?µ^ú¾vÿûžhzzD¿7öˆ‚XSÔF±ã™Û;(q4«z5ÑÊ_YÔSóÜ?ò¥µþ»¼¤¾_ÎS \–ÀVpÑÌè¾&¬°÷­’ÒÖš6sŽ~YO´fl7ÿ߀›$uàtœ—‚öÛI–v».[¢Ð9dÀ§‹Ë_@V|ÛBrPùXY`¿ï, \KŸìݧñ¶&X9ÔÂaÉ…}êñr× k%Ž·,ʼn§ –¤¨¤˜\Ø5D)Ø^ƒu×"‹-²¯‡c}|N¸œ¹ûÚ7ìõü™7¦Àü£ 3Þ4ÙiTý-äÇɾNà‰»pË'dÕ½•±ìÀÝŽ8מˆwg4£–øöcàɹ®SL+#¹Áà!À¡˜·úÓˆF~­¢FVoÏÜÐoH¦ ¢nÀMøù5—>M† å&›¾hüÍ:)¯(ßI NÚmíîIˆ2ðÛZúÊ.:‹>8À,ŠÝñ?„õÑ̘;I¹D¼CF\² S”­Žúœ§üXw€šôibSšº{-ÍTûLúì&¡‘öù[ êkúãùT:‡“pî„õÍ—âæÎÀÆH¢>GXBÔncUŸeH¾]Ó“u¡ˆÄß_»vŸF‡×ÎF]ï(™O†êúšÑPLèfz~DzÍZŠÄS§ç SÛs ±}Â…®Q1Kž*ÇìVF<Õ›³â’Ó¡Åꜣ§yçŒû}Ò)›Ášb7‡zªôS>{ÔVë¿Ò|ÍE†P$ W#-0™•wúž;¹QÓí;ǹKk‹±O­#öõ¢õÍ’Ø‘Xg¿zÂzÏ—²äð©QÄŒ™6„;a(*J˜´wL@?9ã)x›†WžS ®Õª˜·ˆ5Uoa4×Îe‰©—PfÒ/‡'(aÖzô €CI®Ü‘úÊ(O>\Ÿ‰lÏÒ1ÔYÙQã{dóËDÁ™å'Ñy\X÷ËÃnôäIãÍÞ9² #»7~”þâ:ŸÏ-¿0"R”qÖLo=Z>€ðB”Ö²5Žnz\7óñ¢TRRàG=ˆe‡±ËÑ©Ùé^Z ï›&Þ=ÿ&üAÂ.ûñÝ𕺊©Ädµ'k¹î% ×n ïnŒ8}ô¨¾‰fð@gåí»Í-ñùµûk¦xçÿô=Qÿ-vEòQ¯1‰¨À‘QDëïЉuµ‡7ïå <ÛŽ¸º|Š£P€ ï`Ιª„ Z*ü1 l3ÛsElÀå©Ô;˜ ºl;‹Ý×¥£‰Ã3ù]DÛ” j>ÚBJþœñüh/W£l:dœº¸Çðö>¹7q‡·$bRXöÇ|tzû#ƒEaÏY7ÔE6ó2nõ/@§!߯qùß{þHñÝ{ƒ.G-q±òö„TêlZ\Kè¸)°°®—£—(Q¶ÂØ`ƒ¡¦o½YTe˜ðñÁ¬gË-*œàÐ\¤W›·Ð+)öëª_˜ÚV©ßãAi]ҪşU´…†\WõŽ3"âFu€êå6"~+†ôáA½ýoTæ¸Ú–œpzÂW¾¶&öµÚ`Õ/Ñ2àø=[ü­™Nvï?ƒ×•`®Fïø*þï«`QÖîßᦜ;J †­!ÝûIoîŒB¨L¤ŸÄä¼åãOÞŸD^$D:蓈Á„,•°w;ËñöŸBõº.X±-±t¥!m.ÖÔ\¸~Ù^¨0¶gÔ|VI䪳-At35®­f‘#ÜN(5Ù¦YO9ŸsKí®æ«¬•µez´ Þ/CýX¹Ù.~e qËÍí+xqàÀøzAr+û¯aÇ/¼JÀ£Þ%2Z—ϧ?'W6ÙªfG“¢ÌÛ½÷²n J);ùmiâ ‹èaV+*›I( SÁäÞti’ÁÇ’ÛqÇ*ýŠéÁòïët,K¯ú‹3DT}ûE´ç#P"|o WWïµý£€È€áÊB™ú]svhªRˆáp¡3 Bá»"{‰€zö‹ïõâŒï&ZâÓï虜^”ñC4Â!îsíAHà(øl§b+5@)?äŸf´S &4¹²¬ØÃh졤Â8"ÏiLOû´õFÛÍ»üö¨¿üb?wÕõI_UxFF]ó U¢#$Mä5™àäEr0Žx}9 ô–NÌ6tÊÝr| <°o‰ÑYêrp9z-¢"«aÓ•­Ü4¯Öhgöü–€•¢/'«BvòÇ{\Ÿ!*¬QÓ/2 …ë¹øÕùûT¹¸;ºs’` Ú£JÔê(^}©§Ýs¶ê€\8?c±¤-‘zû}ó´Cü^C‡[]ü“æ6í¯Ž’õ50ék`ÕËpYýB†$®èeñŒ—¯( È>ÂÜú®ð÷cÇ÷Cf&Id"bva XO(¨°®$góPáѯ ƒéÂ1XëQ{¥9~˜]:ž Ž©Ê—¼ŠÃWÌ•¦jQ›Äe¥6ƒ Ø»z.ñw^Èw‘m†€ºß es}«ÙáY¯4‡Hëeè§õ!ÿ­)w„(OYðŽ%sÃF9‘ý ®dÃèßMKG¥8é G *ia­P€ßx” Ãv)ÕŽ¹beÇœ™±´¼Ž1fÒ›\¤q´œdæâ3” Œ°€ 5¿d“] ®h KsQ+Rˆ5Í_HÄð2¡üÙ±yÝÓîYªDÌ7›ÈÂÚz·>s”¯¦] Ñ®|6xpÊŸ©_¯z©#-ðÝnƒ¯E¯²%ŸAe]„ÜÚq¡y›änì…™¦Üý>E­ÂN›”«×Ÿ Oû,5‘«UàʽÓÍmÚwúk¹ä µµÙa’ªZûÝçCæXû„µÒ”;‰’&ýÖº1èäÆêûlrP˜Õ‘¸¤ärª¡·K0î¤ð‡îàôŸKLÀœŠé4·Éàù¦ÊdNhT¥"ÆA_A„É ïGªåý$½;ÁòÙ5¡:Ú¾üÚ×ÿýðÀ›•Ôãb…«?d;\½ÆF#`þÂÒ’LdD˺h¬¬ ´qy£'2ê0RpSg“||ÊVU(wxÅÖ{»8 ¬ò·ÝÂÀ˜­'ÖiMêZßn=I‚1ÃR/¶¨¾×@µ¥ªÄÖˆ¶/”q6º7×M]§ôÒß¹Ç÷i3#èš­øÁ§˜¡Ð®}O¯)Uï¢øp#ï *S'ð„Õ.Öæî[ÌǬg€% h½\ õa÷ç»áÅx ­IÃJÃ:þl\Íܼ”ß&J{V¡ørÏ#žSÇÍ ì'½ýfþ%úÚ^„Ø®d$i¾9Æ ½tâ «ÛBÔeî˜&)«¨ùðtP¢É=yWá¶¿‰S±˜Àp“˜cFÀÕŽc‰ AÒg§ü^ÃÈLgRè}£ -(¹ÿN¢Á©–¶.]V¤ÁŽ¥©jI,…wÄsºÓPÅ ©¶ÛoáǶîÙòäm€™ŠóvT5×Ò²kjÀ{=É îZfIyyð¥.úŠ«†ýg ­)ÌܧÜaÄjvÖÀé@ÜML¿­ÌK nÍœÖåÞµ!¿c’31Ы·"# ùÿ6öæX^=/—ÝŸNÆ;RLOå÷Ë_†@öQE±úO°DÆ ¤Å©h˜«•4µ/$8A^ÜK¯¦‚®æF:Fl†¨/‰)hy¿ö¼UòQL‚¨‹Qã—ç¬9žúW!ƒ^¹_{$R´QªóYc(+55ŽSæ=ÂY_s`ýVµ 0ß'_LJìL ÈOBf©uÁ#X@S4'þฒ Oœ¶ÀÜžŠÌì15É:7dÃ+)šóeQÙO&ý Äò<Û\åd|ÃööL$PùÜrË _ëçê‘݉{‡¢á«mÜ•VZK½»ÍÉÈ_–Ë$° ؈²”ŒÉ'MÔoÀ¹^»õ³ÌJ´ã„ ð3{ŽÓFZô¥g1ÌXó=KÆ2#˜ÌÀ¨©Í‚Œe¾jÙKuÝz¦Ùê@’R¥¼b<9§ß$¼`Ô¿«3­È’)ú½<årîÊŠþî™Eîh’QJÁmgªA…ë4g´¬4Õ¯ØȆ¡wýòŠ’"XË3æ «Jª0óUØêxY£·ö*FºÍ$|¸DÊpêûHâpÂñ“1Ñúþñº^W.þõ‰g´²64À‘‘šÄ.–}½aМåý/ʾ9žPŽ´GH°“çºu@E¹¬õ.Ý1>çÙJý iKŒÄ"(*„$´ôÒ7·ò€Ðº&Ï8iXŽÊß5h JÕñŽLµJ@¹98áº=%㿦KŽV>Ü, !¤þŸֿëüî×'±Ü1¾ûùpPžË¹ˆo8֘ᴿ{Cö8½³|Q•³gi>bèØb Hµšïµ\Z‰‘¯J»+Hë#/jã |-ÑÞAÃÂ듋ꊩ÷3OœÙ×(ͤºa´i‘Hõeb‡†D¢;6+A1±èžÈvu"® ù*¨ûbH=-@?¿qU ¹ýxò?þ逿jjgQ?÷Á Ô• g”u)ð»'³ 3]W˜«#Y’ Ð / ¥O÷7” oU¡ñOZè—± ½°°Œ½)5ýF £j"(îùòËÓxFlåÛò3úÁïµÆ^*›¼6ýùú%weÚfsd o2‰’`< Ň\¤‡¶C€>MÍ>zbUؽ5¤“ü™Â=š‡jx7 õw_©HÖRã\Ëa îšÐ좛õISª˜Oùä·d¼LqÖäs'Ç á›´BPÃã+Øz×1êzÄÆÁÙš‡þq¨ìpÏ}þĉ¸I0T’j’âNjÓL§ü‹Ózø©¹ƒ|A‘b Ýlüz5b3k8ï’y5±k´sPã“¶dÈHµ‡=ˆXß_GTÝbW~ Yñwk9ë¯däÔ›x|•A//.…' äÍuD´ÕêšãZæm­^§‰!m•#$@ÅƒÈ ïBö=ÁŠYbNmÑñºv¸sN:Ö ‚w·/~Í¢¸Ú•ϯ̶ÆÅâ¼õ/iÔ[8÷F]I¤µ|~Ü7 JbëdÀ0¶póÛìŒ " ±CÁi|]ÉFçdP¾-éÆþÎ]Éx#Û0H¸¹Kð\uÐ{ á¼ÔÈDˆ ’Êk0R äÜòÓÃPá  '›îÿ®݈¨]Hmþ$Ë|êö— ø°0‚óÅ­ž|kú{Z„ñ*[œƒkLM¬ìÛÅEŒ~Í”ngäÎ,ÿ*Ž1ií©ÆjB~ÚŸµÁ<<Ã\+0ôº"O®®~êûÉ÷8òù©§Pu‹¨Kx¸Zƒ¤Fš"¶3Ò& ÄýÀýÄ‚ q™á’…òÌ­)BËו:¿4«‘ ˜³0ôVíkðw4¢2Ơ㸠°GЃžr§9U".¡&Ó¹z{û~ÀÝÖ[LôIÚ¾ê¾êô…u²ÅîAšj¡iþŸÒÐ@FåêÓ_} t8w (nTKœò&j—º_%†d[Çhë7—UGïiÉ­ðˆ`gln™j:Ü„z­©s9¨£êp··çe€âËÄùóB9Rƒž<´^<ýpš—ûU'覃ãêt¾§ÆÑ¹ïÆÞ$ñ?íyÀ8%±æíñFËÐ l¾V±‰ûÔm%†J‰» ÙẄ/TU»G# ö™ üÙA©Äb¹yóÂí¼s˜NÝ ¾6Îw.ýéQ%y¾TöÏ*T<ç­UõŸÎ"²Â™"ýóF·J–‡I¨î¦¥ø ÚgÃðªb_`9FÍÞEN2£b*d »+°…zœdUðZJ»ÊÛó_[DZ_-ö¼»â„Œ€ñècûÇÝ":­‡Â}]ÿ Œª'›¢í0îGµèN4Wài}w—’yÇ<*{<6Qƒf›ðNqÀ‰ß¨G¡3Do»ôâž.Ymª³”¹¦á#Ï8sýÍõ ˜±Ù•¸I'í¸)Õ»&é„¿9µq¶Û¢ƒ÷ÿpçN+Ð1tÜbzÓ ¹Ñï·âɵ–ÜXy§w’{EôI¥_ƒP0*‚ÿ©.Žß–…a©#c+ë„ð<£3¿¦= ÿb¾î([@g¼Æüþ‹$ÖI£ÆhMlvEô(’ ^FàFW•E3füY,8/%óVê9]°{Îj·GͲû½*ëPPí{n}Žs³d2¿ZýÓÞµ•–ü˜~¸ÒVfW:=X§¥¿iÚª±4i¨øŸ"ÁòÔCóªE#–×OiÎKä3ï3ù*êÖ51Öc ÙZ•þçN:¯…¹³è ÕÿFÙ§ 8Ž÷Qõª{Ñ€‡TJl‚n?-AØŠš;ê)¦«½WˆlÅ‹9(­^M8å|¸»*t0ÀÄ€yžãLéžëÞ.2±Œ«&ê‚_¾PÀŸ(©¯9!)ÕÚ¬Â:^þ!¤ÁpÓÃì0Z7w>d¬>µŸäÒE#çøß¹í ¬^ TPN’Ù Þ'ôÏ󃵵nX³\Su±ëà4\º,bY¤Zié "0¦Ïš9âØcÄpÑÉZåi©Î#)=8ö­íäˆr@^ë/+êç.ñªæÏnÍð÷½´·%¾]ø¤ªðƒ•¤üÐl~FÓ©u”ÿã2¨¸ËáiÚãœ#ÊqîÓã ½lÛ,’5TQµáÜÌ †Ã<Çg¥Ý”ÿ!w¥xTñˆ«PŽI@OŽ®ŒhÈ¢î@²°Òˆ(ÆbJc0ÞÈlúóörµ²I“yíÐ¥våÅ·ö¬ÑNý~€´°Ù²œHÑ眄G)xŽÃ"AÜHþoÆi]¤Œ*â¹X@À¹³x¼CûÞ°¯-ÔgÚ]Y+~#KÝO¿ä££Ñ-bR}¡¬Ñ;W3Q &¼åeó]9¶’õÉWSœé£]b:ªŽÄ#c!+F_î@@³‡é ƒ0Èò 3ÄRå>ví)dbVªX×rj °Mä ö¯sÉz*Ãé&ÈzäOw“uåuu`„æÄÿô×Ð4Qu?f¡J`”ÓLj|[ç}1S[ý¥Ò ïýWSÈÁ,ƒ²¶ù5'9})"!Cî÷‘cskèÚƒ–C]·™üz¡Ýø@o~`ô]þM™Í87¢Â­Õ¨±Ÿ“n¥g;©Nz˜ ˆÝZ|•ûÁ…›BGü ¼Xø“ÏÏu[)úž½ÿñ4毆f{8`Ä™÷ýeÖoI¤pëõBÌ6€ÓU]ô«‰ŒŠéÀE`y?åßÕÇ'¶iwãÞ&ÊpðŸM%°Ù!˜Ýã³çbBž„ñÔ&ÄúóÓÍ{/ºÂ´cº!Ùºlãÿgá(å ¨Ø&ž{@d(œ.­áž#QïAb­žõ§Õ¢õéˆh‹kz쾌­Úß« "‰T†ÛôR÷¥ÑSàÃÞ:ÔL®Ìô›®I ´ˆ—ø4‘'[ɼÈ<6p\Køõü°•æs »2¡ÊŽC¾CÛ6Æ 0E“Ö7iôU¹KRÅïÁ³ãåäiÀn¨. ’§ìŠ'²ÌÁt œþ&SË’j`Àîi : X0~ÊÉo§ºeCü­[M»a—ü2ûsò£ë€‹/¶åºDpÜ«„•>Á̃™¨öߣ«)ê’3ÇÆÍ%]γµB!{¹ù™ŽèM‰\YrRƶֽ¬,|FþŽ^”´h)Œ^vû'@=má#ýÞõZ‚CCAE$'h«ò›œ=ŠÓ’…‘|UñBd€ds æ !G­o LSsÃÿÕøŸ.|&v”sÈ “…X1§•ç2_¿}–°b\†yAUf~ü‘Hˆ–º§6Ç€c]êD–8”åÅAð•A“õânö'çô)A;œTùvô?*±ç# ýÌ¿œ`hö –æÈ”Û»ÌRf0ªùHõk]DáyñšÉÞ_],zã‹?IäigMÙPoøÒÛw\°RûN¿Ð4ÙW¶†ÙG<7MF‚V¼e5°ßДKf´¸Ud3Õ8=¬´êÂ%©/ýG†¤žÉÖ²[ûŒò@`„^÷S&P“}Iüo™ÕTC"·NhOä0ß0¢|ÝYIªžé)ÓÝÐÓøEâñU80ÔKÀðy´nGþ!ñ¾ßOú[°ÖXBSÕSÖì^0!ŸP~¶ãx¯À^ºëë¿–À Ç#Ѿ †L›~ê>„ †y7 ¢N‹ßÿÇÿP t­$¸p« 0†”Zø¸<„f¦Ô‘°–v‰M±‹¯,ÃBˆØíÈuö?¦SØ•¥Èn­'µ’Høšs ›kÅëÊh™Ñàp¿ëÁwŽj½Hý ¥~Õš/h‹«ýCèpŽ}§·4/úY®Êæt² DP§%ßçLLPi¤óö!{ɾ}Š$·¥|Œ¹³å‚¯Qƒ©ˆG¶äA´ŠÜýåžH'6bÜlt/;ÿYM]žwü]ˆÛLVæ4íôÝ šå"×}¦ñç3Kg+AÕ¸µR,#´.¬phÄŽŒô÷ªu¡s‰Îí’5`“!<©7 ôpjÂØ†Fý$èS5ûû;(’2U¨´r‚¬Ýú+³IwT&—~¡ßAÅ_bCtÕrÅï³÷C`.Äaµó/ˆÚ¨ùúýâÒLÊÛ‹wmŽ<òý!»eÖŠ·Óéh b1Gï–¯]:žvj ™=·8óõÔHŽ×'8œ×/¸é ú.+ª1É ÆT£äh¶#éªò\zc§ÃV]ϙʡ9‡Ýæzôn¡}ò¬å£_w$Î’ç,¹fi{äVôr™5Û?ÍÄ·#lç§šl¤9¸ ;¦3ÊSÁ¬çZÈigG~ˆ€ÖtŒ­ÉÔSƒtò¼­4ì=Xbë²ß¬…OÐÓ×KÅA~ªj÷X ôh××pË‹à~’)² \û–äƒÎU·†§D¹”;Btù ÷Oý^éü•ݼKˆ¸–šKî9š–W×é3À¯úã¹ÅÅÈí§T–yj”V’f{-™à·÷ñ£‰m<ž>ùÓ/â æJêß¹ÿÈÃw°N²Wy‚—òì-ëÉiÁé'Æ%5A”Óc˜ð¢YÏ^è¾ v^âÚêJ_B‰\ OÓŠ®e^€bF÷Â^žžfÆ4éè v20 EŒmf• Ò÷oH[Ã(KÿKùÞDå»Ò«Š¬1B‰Áeã#³¥¿š–°2Ô£íÚX†YÎQfÔr&´=7ýȤàâ­µ}ÖŸP¦‡“û y¤+„3ÿB$½/ÆíéÉì%dƒ0×±|²Îŵùµ¥”î~Åuäaµ;´6Ô æ^”k†.‰Ç>NL[µ¤Ä{Øx¦ÅÎî€þÞïWqdî¶aD6 `Ú¥ÍIàð„ÿ `ÛPi%i5à³t[Ʋ¦ LÏq¨—L*“N'½…RŸÈaÊŽlœú„I=‰|}W×ÂPF5ÊvÔ öá–Ti5©—ÐFñcð§*eð_ áòh ì m¿³'ã0VðX8ô[^<Èv®'N/ˆk.ä7>f{W$ j·î¥Äó/²¾uB¯b1Rqz­pÒt}íÑD‹Þ`fƒ¹A VÁÍÚ«ˆb× SCˆóîSÝþ»|Ñ/ªË•ÆÄ ¨Ú*f>³žè¹:Âö*u‡ã’ì_p íh!£p=‡o·E0ÊÃs-¦ë°BtOéá`þn6—íØ†9°QøÑo£¢ž!»‹8Ú¸R»,p™…~QÕÏù‹;Iäºp~ÇAÛ˜e«>ÖzªûÊ Ä…•XJ—3Ô$ç{>™W[PY%c0ÚÔC6‘oÂàa,vÑé.¸òU/`ÎÉ\6M¥K«x*.dľ§cœ\B:r³çÌçâ„#cy{\uÕ²0ü«µS?d%…CJÙcþR¨\¹h¬‰ ×€l4(”)k4úØt,WÓÍ9.Û&JU1~‰Ù’þ^âÇoð?j#ç Q8]”ÑG& ­–B“^³cÖm>YsÌRQXTÈžœâ;óÊ9étµ–á18’®4 /Xå‘ZÝ ÆøeoÜçt˜oÀ|vÓ5sëH¢æHHë§T|,|á†ò9?^}m‚kš+üêF¹$è|zÙDHÄ„ +µÊòµ±µH+eÁ®Ç õô°¹N_ÝâC­)B%á|]e8ù’W2íó1%ÏîÒgܦÃî1ÑÈë:̬–‹JÚeñ& w&PFvÂ9¢Û‚s‹ˆï.IÅ׸íìá×V •ŸãÇž1Rå­º¹p14ž• WÖß¿2j•û8¢p,B=t*ÁKcÒoée¨‚n‚‹“Õ˜\┿»û8 à(Ì›$ÍaÝJhGp0Æ«èTßKQ[§-SÌeìÒ½1üp¶tÑ9)»Ùž‚geÿÃì ‘¡ïYÅ'ÉÛŃ[;×élè€_ƒ{·cTo4 Rc¤Az^ìY¯.ü^m¯9X,Jq8a«ÅsnötÍóÿ{}°J¸…c+°èäQçÞ,o*q«Ã+Û9/°þ–Îtaš0pi>ªõòUÐÞ8ÃR†lÜ¥¿ÆÖ¸Í¡EŠ¡Ló÷Rë¯4ñZZˆ³pㆲ״%†±í~`VPàûÏ%@©ýÃÛÉõ+~‰óf,³õßð¯T˜ã4µü9Ç{æL÷‡7ìäÝw²Ðî)мÓ¼ùœ2ÒXzíð®<|Žïò^G¹>x‰¿0%˜+ ãýÎÆºœÖúu‡*žCñ¶øE­‰CË$ÝÌÑÞQÏöSüP…øæ°— 3‡}ÆŠé¼¼Á§:¼ÝXÌåÚXýó¤Ãr@ȉr¢o~¢OrÛëgÓ6jKÆÔgª¢¦ÏmôgÆaÎ20êLÌs7ÂV*Î^ÓÖ-ÑtŠ8ü¸Ï]Mì.è¶ >r(7a¨Ü9zŸPŒµIr•È0âQfo¦j×#Tº|®¬[5Gé¸:áëäÍÒ o¹ÑÞØÎ•¥~ÁÞn8ÌÃkå\†lö, ½âc+Ï‚£;í~*ƒöî—¡Çñh¢b 2´mPé¯Í|p:nÀ‰w¸Uˆ÷!Q6]¦v¢!ËÌè¬FÇ,;˜Ýò¼TÐËåî>ˆ_GÉQ Ø8üUæ¿FY+ -f\ð?½ ŽE¿1BF‚{å~¨“eœºíåÏ»>˜œÑ†î£:ÚnD­/š„Ø5ݓԚëÙd×à@ƒŸ™¬õê0pñÙæ6tÄñÎ~¯w^tü«”;½|âfÖæ-ŽÞÛÝÄéY.hòƒt×e(Ã圛C>SË.yʨÖM9VfjYÿxš±^}ŽÂ½8˜¸.ƒ¶»°9~I×Ûcž‡ý+oz]hß_1 AeñüÊ_Ö p–7Ÿ\¨C±Ü¥¦ôÔýÒR7£uˆ. @üêÓÜeåìõò&²ætßüâËíØ38zÙqq”ô½vkBAgüÓ–^Ɇozxʳà ºCœí@8ÞÃsG¼ç6Ñ›â‰ÂØ9³:±©žÁ„„ß!tV©„Œ9k<øÃì€YhÔ×È .fEbÚ^ž«q`ÜöÄhbFöÙôKFÓGüÏ?óŒ©aíéæû¾­>ͽ٩¡`ÛP—õ—Èð9»áw ‹Ga$¿jìóÌF$9²2óŸ²aOóíaÂ;.eÔQìßBº@ñ1ØÄ„¾•(÷cìÄy>uôŸÁŸÍ ¿ÉXËB‘if ÃF¦@Á§Ê¨?ª__cU*Øf*ISNÿš õçR©-›ƒ5Þ6ûÞùÞ»ƒÈ™¿ãÇé.–!K¡{ÀA§jIl°ðþ$Üt¨²6gŒm¯]5iB´’·<…mª¥´w%Ã’g–­’}†7Tõõ€ó Ò°^}_c<"]ˉ"sEœ£™'Y!iGÁG ¦­/NrºÑaœDÛ)†&´¤¯*4Oºçuõ?N¢9rßClG¯’²½«Ít!âK;, -RÚ;WËàST0Õ€*Ö˜‡X·‚86Šlpæ(byr›ïdýõv¾yà@¯aþÑ÷&Î]b6´3™úœ¼ÃaZGÁŠ«¬P>@Œ*¢î¶h?@°FzÈ  ÞËç9~óì¼»v ÁR:Ôû$B"²³ÖÞ‘!ÃÕï"ËÒÞY!²«ã±ÈmK¯Úê§+¾dý¶Ü´|^ÁŒ£:ª!În/LÞÒ®JeÀ„À d#{a²Æ×ÇM û&`å%=ê¹Z,Jè±{P® F¥èdWE`yLj³ù^ù«ÛJ K’]oŽêÈlM3l€èYH¸ä½% *˜|û,ëõ/6Ù/°l1^g[ÌA«¢’<·ÃÞû«tá‹ R–f¡‹\¬©¾¡¨Í–\[·úϧ6HœƒuZã‡?ir­—û—B´*‚ÃlLàï¼&C¥oŠ_Ñß» qy¨lãîëóéºq¹õf.>£.AÊü•Ë€io¯,ëxЙöÉÃd™yë1GaåÞ·dŽåfzÇ t’ÏjçUæ’g0¨)Í"*Cõá íôð° ”‡±x lâ±nÝ¿ óf5ÖÌ`?Mò]«­ ]{¹IBh½à*˜ÅšÜø²Cè,ŸƒöãGº?»„LêóããAôwž­ß†(ceù†ð_;Qy'žøš_6Õ§ƒÊÄ6³Côâ\`†éðZÝåd!IdÞ‹®·Ä‹aÌ¥:<¢zrÃîYÖ$°ÕýÅžŽ©s÷±æA¡ÑŠ'Ýlg±á(crF‡´Ù˲8ªÚ±Æ“?—x¦@=D´6DçC™°â;¡¡ŒpYQœX4Æ•ºÿA!å”EBô“'èµ÷,Áä×ø|Kúq¸2¼¦Ï}åK¯è|ÙñÁ]»7>Ë„ï°b1¢K‡±iÁ[à]ýÂsÛ¼ý²“Èä|Æfµf<ѬZ£¨¡“JU˜FµÖb:1Š:“ €‡·‹9Mù;(æíµ¬“Íû€†ÃMñy–¾Œ…]j]¯“¹¡õø¶@ñà®,‹gx@e&w·æóÄ*0YJ~îLXTª™#°lª–R£?:d¡T8bM¿ÇµµJ܆ÊÓC+ÈÈ–@¬‰rãï2,Wx dƒp•N':ÝTITœ˜M…ƒßÊêÙÀÃUDÆ—m“IÎJ¹ZOg›æà cA–ŽË\ªç°õÃã-»…s/²áãc(@¡;äGBÏ:a‚8¶¶œþEû ÃTþSËKàJѱuQ¬‰Zf+üÏyȤXe÷S¨šK ”È 3ÀÅj=½iù=VÙNÌó“#¥õ¼T‡™³Eƒ¨ç}WPMPÙOw¨WTóéYüà½1ÏR¼ÙÞ‘OêfsζhoCÝhá.pxsŠjPĀχz>k·ì-nA« ü@•ŽãkBY3ÌÐ $Ë¢@!»Üo¾(.é*“I¡ê€vž!æ£ç*è.¹šç2Íö‡î3]ÇCÊÌÔYöö«3Ò˜F]q÷4’K íƒÿ„8Áƒ§?CNj„Y”…êùvÞ?qì¦có™&,,¬Óû+)_uû¥ØB¿ðP…Ýz¶Å~NpqÈL­yfaMçß¾!‡ó -9H^ù1zîæªí<»Ïâ˜QºÖu“‹ò^Dpf×È‚€ˆÀJGRÃÖX.t¬U–ÔeþoLòæ«h¬ÎÍîwO'ÃîHâ–0f@”;ÇmÖ/ÿ¬ÊfcëôÞ¢ 'éCÕRöm‚}Z)%$7uj4Ñ'KËÌ™%C`(ܸb×ûK¬í6s‡Ø|2HºP﹓u-"7Sh¤À¿š2nD†€úéLHrÙprÆ•z€]Éé²´“= 7äÿžèçm‹æ`–“!o),©ó¥Ç4kž‰«ê¢½üf0ü1uUŽ+ù½ã«‹Ÿ½W¦3Yþ¾2Á€;x·*αxwQÊáÚ„Dr:&ê¹ 9Å¢óÝ(ÎüÞƒ¢Ýö8 gÇ%™¬?Õ´Œå&W¾ž5Ëø®ãZ}¥\PÇÌ´…ÂÄ ¡[ 1¡æü¸,l$› ?;çLžhÓ‘¥qPôAŸñeøþ|’Öm«3ÙYœÞ ›s»›†ò9ínüéQ{Ó-nLß³és‚ÙÛ_à. ñ¢ •ÃwD:f#Îy‚,ÇDÑSÈÎ5›¡GSò÷¶¶ºïVtorZÓ uqx¯u.ç*z¶mý™Ä±íá*òÀTË“i cæW„ÿI °¼Û¸úÏ_ŸùÖ~Ô»(Ó)oÕ!t%KëÖn\Ž Kžy<» ’ÉPÈw.¬¯áÀ:óèëéÕÿö^Ø™¦‘ˆåúš)œH"¢»DDìy`›+%íQV0×ï&»:®DnxåÓÙ&nó´¦PÚT9¼×˜©àðwù@ÕFH„ñâ¼§Ôì­…p4XŒlŸiðŇ ío9&V :™åÅÙv .ðE~JR\QݱåN惠lRÒ¦æ'kä?O‡…½£´ÓôÜdu€5p]m‰G-\yÛãÿäöŽt§R” ‰/²1!œuëÔ&vXè?Ÿî0o¯rP²çR»<çî—˜‚³‰mÛð‰œln0ºnj*4U¡«kOÖNø×++~8tX¾^ó(Æz醋-únimü±±UÍò!7¬ø¶».#=°CVóy]IAWôåo)ôIh^ñÒG]óŒÞ€Š‡BÙ$Ý]¢;÷ ›ô…2$(˜êC³Üº« Ѐ)" ÷Sêôzñ"lxhÐ3-λnäIš6‚v‚ú#Æ™ÝÆµ(ɚɆˮ‘•-¡N8Zé B™Þ© Çáê65{ÿp‹^øˆqQ[‹©ª8·(€œÆ¤ÀGªÙEv¯¤™ œÌbˆ¬Ð#LyÍÃ-¯0ü˜ k¥0ÓÌ* 5i„ sîÈDaÔ2ÔdËAcqúý§ÿ¹ÿ¯é,ðhÔ£m¤ÏÍ+’³²,¼{)7õøº<ˆ1…ŠdkœqaÖh_•}esSæ‡Úx‚wCZ-à·~0&˜ ×¢ÖzûˆPb¿¾ýh= RO(&{Ó_Ë4ϯ“Rx<…£p,»/x_ÍÇèYÀâ4»s°:¿ÞÕœ¹ü'£Ø™im7Å'Œh±"pœø«›R/¥†¾ë^aÈè-0)µÞ(}íûôW—o¾(ø¼Ùä¡)jÐYk>‰´.ã“\Ÿy†„XŠþbÏP•È1/ÛH§T€ô°œ}¼µ˜¿£ÙÜ[ ·÷D¯Xùk©r‹aš  ƒ—Ç–êVËÿÅ~ø…¢ŸZ2™^´î±ºðO¨emßéŒVƒ˜³%”ÙSæìí*\4?H=ñ=Ë(0íYá”%K°rO]þŠ6Òè~÷y(-dr×wÃ[äjœ^·t:¤aíÄÓ{½¢³1þD&ÚÇ2wsçhí&Øæ GB ž› »A…=¿øUZä™7e.ˆ 6|-¡T’nq´ïPá$ÅÄu'# øË—›Š‹8Kô~KÈY;“jÍñ6â••^k«„Ï!†HG$?bÜF9©ÑœIeÿ$)èšåúšRÓ¿]w¼b^H7È£þº5?}õZ±=Iü.0's´ x[rÆ BzæÈð:)\²ê¤Û—0[´…›rRÂ&··EÌ,µ$H„Eúã>}ž¤(çù¦}‰+v·&"оÁŸÛ[RjLKû1,½r(’Òü×£±³øq7µ@×$[Œj‚‡ X&Qçåê"‡÷Àpó®’>¯[š3Þ3x^ÆÓgºT”™f„4\º%NAÒ­s4›àéÉbyXøPƒŸœž™S·£[9Äø-ëÎ k×=ž¢i;v–±É_=I¥›ÑØ´QÎëÐÇz3Ñ6óC{©Ù™5cˆôûàŒJXÏS1€Qî-T3’Ã.¦º‚¯|œEÌcÅ>E⊫)œ$ÅcÈÿv°bΔ€ ã3 "Lߎ×ÂÐÍaœ“Ý¢õöÍ ™[ݸ•^³¾|© .¾é¼Swtcf¶1¢ˆ:&{ŸË›UcÿÈ·=´BS&Yw°i º9r ¿æe(ò±öKÈ1_)_ŒÍ(?â&ÐNq½æjÀA™aR÷g¢ Bê‡Bâ»ÅA=CÏÂÔJEHûêÒÆÆ k®êgܯ`À†m3î-KÓ2±z}µj¢8§ÉÛŸšEÆ»’˜Âî, £Ù¡ nJ3ŸÖ ô¹çÇDu‘;¾yVÝ®‘Ê2Ï*O0¼ÂÑC†6´—SŒÀ~¬Òͪ½ü!N¢$’SשzüzQ_é|‰2oV”@×p0Çç'?Ùà ýåˆ?Û &ÎoW_—®Þx?ئTȬ™EØ_Ùˆf€³5]ܸ¬jD=VY4O¼\©EäËÚ3ÿ…qÔíá‹E›„Ñ™ámÐ,p£/Ñ+ûÀH¬¥ ™LðÕ/„©³r'ë¾>ˆºó€†Ýx4TýÊGLöfo9éûétZb³-š%3~ç߸Ζ®+a¯+»Q¹p}»6YìRç>€ý%üF$ ¸ÀàqƒÙ4úâlÙ–³í€Æ“OÖuö%qbïk"FwíX1Z2YOSòTÂΜü‰‚εî Ô³© |®/t D2Ìm8ÿûuÇæ„SÀTµõŸ«- wí•·:@&"W XMI˜&db°­I¬ç[]CCyëg&Ø´Ò@V' Yà7 ?Ìl˜²(çmj  ÍC”£}-€3úÓDʸz—È ºŠ6iŽÕÄîµ6/º'²] h ëGŠçÔRiÇÔrs|˜D°‡h<¾§OwÃÅh½†È†èd‡ÍÎRdi§–a%hO 7-ŠZ„þYÌ ÇGÏá ¦aý¿Bî9—BGúöR’*HÌðàú‡k¨\âoMxºDŸ°,cJí¶°ÉëâH¥Á!¿à¬<̤=+~”ä{/BÒ G)Ò?*æÀøX7•4N¢)þÁnþS{àù?ÚµøLf",Q•wÓÊ1kòäRzXCÈéDHpâöJÏæÍóX2›×K©/œxT>›YÓ3D?Üÿ›FÈôRS§Æ<µÍ©[Ú«ÑÌf§f„÷±¾|l ÏD{è4> f8ñNæÍµþÝaH;»þ.¶nPj݈‹Æ§”bX¦€ùðvw Ô•ý”X§\]t)ZøåŒMäÃ Ó °¦}öù†”iõª¬Ì¶Õñ– Õõ[«ö¡¯àFsàDà]<½'=lÃ:¯VŸÛé…ÛÚ«…‹ûÁtCÔ%Ô¥žé }p=Ýç9èÊ©0MúÓQOç“‚%83VN|@øÖ¥£ž:mGˆEðŸKžÀN ÈrSëünƒyäN™É Â#nNk¨·÷±Vã@=?&o‰Bs}~¬FˆD±ïWЛÚVeõ=,5LoÉGuø”,òj‰˜§…¡+ƒÂ;Gåé[b‡aY_oq¿] X#Éã=Ì=+ã¾'ZG Ç:•ŒaI*›eé¦Ëi/¶ç’ætFéþ/[òô'J †Ãþ"¥yæÿ^Wú»¹€ä›aÜKnNXë˨¥…˜Ü7Inso$Â^„‹ŸßÀǵõ$Tåð!_åBtV3•þ£‰ MtPææ39z< 61µ¥;'öÂdɤÛ× 7±»ìvdÎj§†Á>u]UkôB8âáü:ýegØ­!$(;·Þ 2]·6 î¥ò®gËÈkÁ¯ ûø£’VK±H)=¥$[€X¢|XHZm†Ï Àm t<^ê‚n§ÄSÆÆDÕ`˹mÜ`oàAﯓÐè‡Z¿«ýªò¡âãfß–ïþx…" ^¯s}c ,î e_=缬ÈYzÂá¨Z,éèf~¾^˜fjC{p¬\dtJãTIzgN°þ¯ýÿûÜ^ßõÃ’U©ÑíÊ·ÇxAIªQ}hynaB·v6C¦ `…K俎Çl ¥1©B„9Xïe’`âŽÒ9_¥\áAT‘_®îŒ×¸ÇmÅ8Úî>PÞW>WÔ÷ï„Șù9`%é‹©çªd­Æ¼¼þÆ–ö Ô0ê_*b  zªl@Õ)üÞ)i¡¯ƒ&÷8;]'~ggw4•Ö&g‰´²ŒL–ÞË–<æoþ•ê–$Ýv[šq´/äÓÞãoXGGQk5ãj œ"šðGcIWqOù¦–~sHèÀUB^­…OÔÙ—ÕÃ&—©þ1ÿ™Ãx“_Î$gLjy¨4ðÙáGQ ƒ0s¤¶Å¡có\Hÿ(Ï® z­2ͥͷókà°®&â¸rFã0¬“7!+«‘Ä`E¦sûÄcÙý;5FCÚP ˜Ÿ·ç7ÃR0ð ‡.‡i¦…%Žäâi}‡9ï¾þ¯C œþ¦bÁ_tQËr?žÛS˜dÓæ'^cß 'Hæb åzÌÅ .Ô4…Û¹A(i”\k; N{Û 4™²AxÈt%5oÜ–9×càü[†ÃQKf>ñ; ™ÐP úãvŘDOu¶I¸#šCáƒsI¸ÛU‘ù#o=MCWmõ¢ŸLq·>WÆé4Fb®Æ+P%rþŠ´ž ßrv® O[ð<­¥«ÓiNð]A†U (Q|„ðÁ3ZôÕ×R0‰8Ë;‹€´®g§¬BiÃèeé]Í2ÔÚ} Sc˜”I¨PÔ’ÃÄò*ÆõëÚµHç wP•?…Š×>™¼üÉh2ç†`ƒ’ê0ݧãfØœe‹´íÆ.T€:µ¤ý Uw™-j)¤FwûP=˜¡Ø["‘'­QYšŒV¥²1H~–| 3ÚxŸº9™ªG;4†¶‚½&ëÌ2…ìÒ¦¶â$ð8†ŸE«êŽ$¿‹®$徉zîM|½˜‹É‹¬cö=Á'ÿ² (1E2 /Év©åáoÊó0(‡ISäìÚ°ÞdaD;`‘xþÍ`<=(nA:nC ZÆÄò".s&¸Ë0®€cZ㩤pö…@ÂÁØr•·dØ©GçˆÃW—k¸Èþ›v¥Ÿìõ^’~&xHÔ0Ç rØ$÷ÝÓ5+X@¾wTS1ÕfÉNéº3*…oÈíR¯Á®Þ­Ô“„0ê­ÑÊïOQ›Üº)pl꡵¸z˜ÆT³´ å¨îó7zý¸sš>Gï¬ú.ðV¥ ¢Ü¸¼þ53üéMD|u›±…ù)8®½ú{ø2^BmlŠqÕ‘qrÏ< 6]¤ ®º«Žô)±,ƒø©a|Ÿ%éõjÂ?Ï€`&¢‚àä(’Ñ8A†UF{H'š«0©S×B 1ƒwÙƒfgªKôM$L¸Ï·J•‘R²vŽýìLßÞèly9Â$9 ƒÓÊh÷ö”üZÄþN  ß?ÈðQÿ ™Ÿ!´º;kPÈhÍZ¸} ZÚu°ïdse7Àdk’ù» ¸ëÝtUQ¿kã¼·ÊÊèáëEiLeËÊÆ8µÏ¯kÎËeU¼]à6‹)¸ÝÖùk!†U„KowO½ˆ ³ˆÍàŸ¿gœc’ðe!> 2v‹Sªk RAù.&,Â9`"#ýõB:c£í;)5z kÈ}ûæ6×µ”Y2Ù—¨Ížà"ƒ§&¸Í¤¶Æúè¹%Â$™(\Ds­\…‘2"Öü8œ¨^½(k“‚} z{üTq¡Ú¢äõƒ°õƒ,]‘ðvñ´h8 B ÀºÓ2­•ô…ÂjíÆ#r ÆÕa åËL¼ü¡‚‘!¶mwdvK~ä'¥"[žu• ûPwÎÊ|å%±… é¾|F÷íÀ­»„Nq ƒì6¨{ŸÞž|#ñ=ÕpÕ§yÆ[¬–wüRêêçNGOŽ"ºŠ9³Mñ@(ÊLå.*‹²BºrŒ\„…,ƒ1â;Ý€ì*FjFÌc— "ÞÏã°ÐgsYABÀfúÜþš‰yyZûÖ8ù<˜÷À±’¼ÑaàÌѧ(I‹d£çF´Š…@årl{‚퇦òÑ\ëTYs6ÝR©Ôã@%hà=ŒµêêÒPYì0r¯&_Õ+“°Y²0uáɪ/pŒð¨ 6,ÓW˶ÜÉ©dœb,ˆwLèòØ”S7™#ºÐÈúìuͶÞt²Î¶mÀM#úËŲ2êwö½ë[¦Ð…D@©ÕV„DšzgÒÕ)Ø >oÊ´©öá‚1+œxngí¶‚ ÷ñ!>–xAm¸×Q8ˆÛ ÞíàWËS« ³)×T :k`î|4íÓ-C6Ô 2B@õ0¼tœ2aºÓ~áü"ä¥"Œœý¼ùC{ ÀY\7K ¥ÖÚ8_!kÏLØ›©¬2&¶lЋóýqݱêj|oƯòz@µÛV© ”‘'ëM Æ/‹›½LÄ2/ÚU¶ªàû¼:ØyMðÐþÁpÝÝêa´‚•pñö-ì ‹,ù üL¢n%(1wl\&Àƒ'ÊsR`õ±:{$ŽW%Íœ°Ê0'¯ÃEJxÛJ´P¼“&›uïÝ´AO”ÏÊ'þ‰u'b³S‹$÷>Ôƒ—¦ª×¢“4 ¶ï)°‚gò=ñ Ä`„¬_z<€°v£.ø7úÄÜ”&û¿úx˜”³9T’ö­è½›k2—!ô@CÓõfÝy°×~² dÔƒRÞœŽÞ£‰=ªžš1|B{´ÆúQé8ÚWOo ¨T§¬ó—¦ç»AÀ°Õ4îYîZ…»L~"=i̼˜a›%ÚMÛ@€½9E­ü'¸WúÕF³ïØ·1=©ó4¡àô ˜Ï½0†ä–ÔiÛiqx‰|ä×SȨ—´ÿU:iþå×ÅÅxœÒC˜)¡u¿)5,¬( d3&ÅrƒªA}|û•! "=8çw)’ˆ¹’¾­\²—‚G{5Sá줪ÞÃPBOØu}°8g5¾$Îéd×È…ëÓÀà*¾í]ycOÍöR)â~»ß)4ëéåªT’}YÈ´×'½GdWT&4àÊ;I"j—t¾U­ÂÂÚ»8ÜŽ¿"¼ %˜wí=C©Œ­„/ ¨­”upÐF¼Ú>ö4;T…Ø¿V7¥í…R_ÍN^äÍŒ Ñ^ˆƒ¸B;¡q‰ÑnQƒÂ*³¹ižqÒ.ò91˜eÞ ûbóìsb>,ø[•¦Ž£©Ã“v1ÿ™ê“|;ñ ijz@sÂíðIã•\8tî–…Èuà¾y%þùšÝ µÈ&¶Ëû¿ãî¨/·W®2Ý.ËH&Ú“ŽÍ2ÿ4=2šø©žôƒ‰“ÀŠ'‡K ©|Ëž`ÿ}ªBòÓcÿÁǵnƉe° •ÉÅ´Xœ'©¹ÌücéŠ^²vœ:Œ©ak¯˜_ÄÒØzÕ ó„X³‘î*ðŇ<'íókáåí ˜jÊpS§©Î7§±J¶²½‡å4MËë©®¼µÅËf“]¬‘±œ“aïèN8ŒûÌ_³=Ø3!ùÛ'ü½™xÏékñ@CðWƒŠÙ_)ä(àYˆ“²¯äF°]2:†@·‰­e„iº6h8Ø=¤Z£XƒD¥‚¸S„½ Ëï­Ý߇4bœyÍ’×U>„üuÔŸÎ0l«¶àð:=n(…Kâœ=VOdG?Ÿ×"¥6«*ríëX—D#J>Ô^"å¡¶Õî7@o¼7zì/rÚ“¾Š]ôqÁ³[ä.Á£ãðüù}ˆÎs.Â`|Ü\ܹ>þݨ”›ó¿¨:½·MšGñ Ô<à)åÚ¤ ‚¤¡Ð׿ǰ›_k -žâqÃÉ«K»ÿ”_¡yú-z¡Šµü¸œ­wœ *¯ +… ¥t`ïH´£BvþÄtôIÖÏx§YÅ«‰O {(ÃÄúNt3÷Kâ½øÀ€ìÏÀÍõ÷B~os¢üc޵p'h…ϼ”ïr 5ˆÚ¾Í’?zÀéÒ­&ï ¡×¨Š„º;¬ÑÖ5Tnüêo ®Gvš-znm>”xÉuÖ/n•—Cã[©­òªEéÞõo}ûv0(I“É*Åüº]@ËpÀ‹*hý“ýÞãlbjÙàs³%¶OO1ûx³™¼È{ü·³žMoj/É$•ÇŸTéëç”k«Yñü?›¯è¦ ²ùþE‹Œ/&—¢;öµúL#<ý0.ÏÅ}wÊÖäjpÉedñrU%NêÓæ”T¢èP÷‘Šc˜W{¦9ðd èÅÒjÃԇ͖0Ƀ›Vù<ÚŠbhôÀë d HKø0ñ³ï»ñ$…>R•YäRƄççâYIVÀÍ^wøÀ9Ì[Zm=¤mÚ‘L.“Ã~¹Ùÿœ٫ª&wÃÇ1“œƒK1‰hu«ºå]\ïµÕö‚]R6‘ür!íu†p1mŸãÄÙ8u%’—I _ŒÄ§Ûœ‚™²6Y7©¦:!–9ïÑ>Y—.s‚ì9ý²¯ÓÊÝF‹€^ -•½½á¡Ö-JR‰ñvÎ8h^I(¨VC«æ²~¿ºîÎÌëŽL£ÚŠNëÃe"aÇ?7Ý.•íôE‘OÆï.t›ÉG–ã·¤9ßã†uvP†3PRH(äÔPôzıÊtÇÀêÑ{õ­å†X<Á‚\ì-7{4 üõ©e)}JÙê€ ò£.Q®N@˜u2E'·& ·À+ñ!Ø.*~ËŽaúÀˆšÛUØn{wÃ3© a/ŽL©«†Ž-ÌÑÊæ›4ã^h¤§ [^ž?ü–«Ö˜0뉑xºL÷FÏ'Ió-}\ÎFÅ…ÃTЫðZðd܇™,hãõÝèfÍÍ).é^ñÝTÅ`«Öåj‰í1öå›ëÃr!AdrªÀ²!*<0.0ÔפIX/5_ã[{Xfßîš_@ZÖÿÜøu·‡Èv§^­Íug_-ä¨ïÙ N|„ùñëºÄ!Òê4g¨éÎ:O~x-nðíðg+,XŒ‚îJµÅû`Æ;)ö¹*Ëyí Ë$Vo÷ú˜§têäùG§ÓS>².@ÙÂḵ‘Ö{Õ ÿ¥ØùpHÂ)Ü_®pµSä${I·µ™™•F˜`B«†RÛõÂ:zG©€8VÐ¢á¢Æ­øÒí[¤€{Ç¡ªr=±Ö9ËWY€‡Ò „T‘]ñtÙc”S ?ƒm,è¨/Øs†Mc­Ûn°nÝ7CX)Cl#qïDæ¶Ì‘ðAôÅ_Ä’8È4«ÂuÑ-­­mñ/†¨°­Ž˜ù0NÔÛ$;,ÃíjeË’Ý+ß‚ñÂÔn-yµ…$KAe÷êêpro¿˜T„]¤oˆÎ’°‹ Ý>à„‚šÈO0\A ͧióÎÚÃQSç‘(E{ªŒüm[Ӵɶ®r4„¸ŸFPvp-"Åc8±wrôs"E‘m†ÏtZ2>o) ®:uFÌÃd×nƒòkHr‹„+È_l!U–•/.5IÖÖ”ceD­Ø™Æ<€‹¹”©Sj”ÕÚ»-\QÇzú™:RNË{&ž$@úƒ/"eñ¬k±þžRŠ.ës {ìĶðÞ· I"\ýdžwú=fÑcçg(’lºH.[×Ü)É"©•Zègó­‚Uÿ´{hнL9Kàv`S5ïf Ýï™^‚ËÄé^8dëohGK¦d ú{HbˆAlä“ïh'O®ª º“þ&{4721«B{*µ³Î_ÿû‚yÊ0!ž@¼R÷OØ´ó•HÁÿD =aÅ¿„s9¶HX®FKM˜iÁ{Ø C-§þUÐR*^Eúà ½±®>ªGªº7—´íû"joK¥\»ÙtL@b£ù|¢k.DîÍÿdßÚ«}w‚@ó7mcÉw¡À¹úÚ‰ÝLåP:lŠÚ Pg71ïת߽S¾¢ºÓÉI8oæ'Vg4^)æ"„©¬-€ÿQ¬ÇC—G8þ^ï3ýÌ∻ÁÄV²Œr^;J6tǶJüG©Õû¯B_t¹qøN®>H­ÌŒ–[ðÂR7NÕ{Gk9ËÇ çöîìN×=pB*¨Ö›ÿ@š¢jгxÓ|Om,Äljñÿù´ºª"ÔNü³¶·Êþ¬¯,‡¢×Us¤y•Œ1 ’_÷˜¨wØ„5<++†6¹G4÷}ÛKû)Œ’T+.•)Ät[Gi‹ SiA›+Um oÞJq}x“:ª§] Ø^Æ¥˜7rˆò”ßšfô`1‚Bµ¼=J¶W…˜WŒ·ÐŠ‚† ½@Ôùl‹)’^MßL(C:ös‚nôÆR^µk ;o:8𦊶ÑB·]!³íŠ×£¼žÓ‚‘?í ŸöUè¢^Hö6Ž1<š¤MžÝ·`¡0ÓÙz2 ˜72EUýúS¾Ý°d¿·óê5ò…-ÓRÔZbUìåØWÆ‘ÛÿÂ^|,$Y)h å;…l3sIu„î‚ÞíÊZê7˜?Ò†-Ú\ÔY+Å.:qG„̬Ã\·r¬ïÃO½CmìÿëÏЩɴX§×°ˆý¶nKL3h‘=.`¸…™6àÏ®ðòX|¶ìÔÂOÀ "±¿T¢¦eib¶õøñÚÙìn Òu_“ñ»Ì̽eþí7C2…BWfkþ>·öÑ>H3|jà€®Óþ÷D{ ɲªæÐŸ´Oß:ù ——NdåG|‰òÊNL&ƒa¼Ò×>.iÅ{ØtjãZø,¤,®üLOTXϬ›Ÿ‹óø6zHøtê'œ^!ÎgÉ´qøå‘¢ÛQ6ss¹øÖågæÛ¤Ù TÒ\,0ù0-uvø3ÍKf†´Ã„–pAd3}°LZµ F„¯•LT9,$À)o²g…Ê^h„O'Ðò³YB‘wð,2=_Ó½¤e•À3=¼r4DêI¼³3§e¡6룕µ¶ñOK鄬é#úIÊq=HòÓÉÃ-X@EïXO†K¾ƒü ~a!"~+ë7Òú·àÿâxÜYá< Íp'c%BL_•ÔKô…£²Gà£3ä²tÌeÖ>òÌ# R-(‚,OqÞ¡Ù@?©¯ýùS~»N1ü<_0hž xÚU›1sRX»–Ä`ä_€*Ç4PŒ0„ÑÌg·s’}¾±Áç¿íùŽŸQ?åÄo¨½ÙT5aÿÛ‰ÍFg\ÕùËØ€ÄZÓÑY~x ‘‹„½3îªB—)²w<&N¿ð’ŠÝñªs•ïÍ.WTx€7ˉ OÌÀI 6\««"mÀAä Äãñ.‚¥("àÔqèZ)eÄ ÷-ÿƒ]Ñyë‰d* ÉwÏæ 8´ÎÁWÂÎõgñô}Uš:«E;½ÎÞÇ;ûÆnA>º¥vz¬n·]}O“î™ÍtPûnÚe=_˜›|”uè½zïô j:DRíÁ¡›Í~bPò†–›7ò§16ÍÚ¢D«£d >]°cPsLZD;‹EÆâY]; -)‹~ë7¡94ת‚8—ÍÄm£ã:ÕGí%{”ždìM½²Î„}R§ïÓuò—›oiý{²MB•}˜oó£.)©±GDÜTÙÚýßøÿЙÛs]3õ+#‡Rû3aË0üKá0rX,G?‘ mR¶&s–¾B2Ë dŸÍ±g®þ;B;õ·Jœ÷úñ:Niò¢ÑŒGmwd$¼âxV56j`­£ÀeÀîÇ æ&`áw‘,nl=ìãOË©ò…à_jmªCº3Sƒá~[ “]jÛÀ÷t¶«!N Þ6Ïpü~AˆüÈ`г090zN/A8€b€Çl€ 2ÀTdar-2.7.15/src/check/Old_format/archive_10-1_nozip.1.dar0000644000175000017500000001021114636066467017371 00000000000000{±W`mwTT€€ ±W`mw0:1nN/A€@%­ýêw!D±W`mw­ýêw!Fddirectory_1€è€èíu€`W­é€Us€W.u€`W¦|€à€_z€ë­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fslog€è€è¾s€Whgs€W ¡×u€`Wž½€9B€w€ñß­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@‡ EVERYONE@­ýêw!r€6O­ýêw!Ffplain text 2.txt€è€è¼u€`W¯Ÿ€±$s€W|®u€`Wž½€)¢€w€9n€nöHello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Flsymlink€è€èÿs€W~ s€W}¬u€`Wž½€ b../plain text.txt€{­ýêw!Ffsparse_file€è€è¤u€`W¯Ÿ€ÀÄs€W.u€`Wž½€ b€_€n€“¿®ýêw!F€­ýêw!R€­ýêw!E€system.nfs4_acl€P‡OWNER@GROUP@ EVERYONE@­ýêw!r€ÏKR­ýêw!Fpnamed_pipe€è€è¼s€W|Ðs€W|Ðu€`Wž½€€w€v­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Fdempty_subdir€è€èýu€`W­é€=5s€W}Åu€`W¦|€ @€_z€ŠT­ýêw!E€system.nfs4_acl€PçOWNER@çGROUP@¡ EVERYONE@­ýêw!r€ÏmR­ýêw!Fz€z­ýêw!Fz€z­ýêw!Ffplain text.txt€è€è¼u€`Wžë€ÊSs€W|qu€`Wž½€}€w€3n€8Hello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Fddirectory_2€è€èíu€`W­é€LÕs€W}Åu€`W¦|€*€€_z€°;­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fslog€è€è¶s€Whgs€W ¡×u€`Wž½€¼B€_€ðz­ýêw!E€system.nfs4_acl€P‡OWNER@‡GROUP@‡ EVERYONE@­ýêw!r€ÏKR­ýêw!Ffplain text 2.txt€è€è¼u€`Wžë€Ùós€W|®u€`Wž½€¬¢€w€9n€s—Hello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!Flsymlink€è€èÿu€`Wžë€Ùós€W}¬u€`Wž½€b../plain text.txt€eS­ýêw!Fpnamed_pipe€è€è¤s€W|Ðs€W|Ðu€`Wž½€€_€Á­ýêw!E€system.nfs4_acl€P‡OWNER@GROUP@ EVERYONE@­ýêw!r€ÏKR­ýêw!Fdempty_subdir€è€èíu€`W­é€ls€W}Åu€`W¦|€*€€_z€ U­ýêw!E€system.nfs4_acl€PçOWNER@¡GROUP@¡ EVERYONE@­ýêw!r€Ï+R­ýêw!Fz€z­ýêw!Fz€z­ýêw!C±W`mwdroot€€s€s€`Wž½s€ddirectory_1€è€èíu€`W­é€Us€W.u€`W¦|€à€_€p€Ï+Rslog€è€è¾s€Whgs€W ¡×u€`Wž½€9B€w€*€6Ofplain text 2.txt€è€è¼u€`W¯Ÿ€±$s€W|®u€`Wž½€)¢€w€]€0O€9€€9n€9}lsymlink€è€èÿs€W~ s€W}¬u€`Wž½€ b../plain text.txtfsparse_file€è€è¤u€`W¯Ÿ€ÀÄs€W.u€`Wž½€ b€_€©€ÏKR€€‰€ n€pnamed_pipe€è€è¼s€W|Ðs€W|Ðu€`Wž½€€w€j€0Odempty_subdir€è€èýu€`W­é€=5s€W}Åu€`W¦|€ @€_€K€ÏmRzzfplain text.txt€è€è¼u€`Wžë€ÊSs€W|qu€`Wž½€}€w€z€0O€3€2€3n€%;ihddirectory_2€è€èíu€`W­é€LÕs€W}Åu€`W¦|€*€€_€Z€Ï+Rslog€è€è¶s€Whgs€W ¡×u€`Wž½€¼B€_€€ÏKRfplain text 2.txt€è€è¼u€`Wžë€Ùós€W|®u€`Wž½€¬¢€w€ /€0O€9€á€9n€9}lsymlink€è€èÿu€`Wžë€Ùós€W}¬u€`Wž½€b../plain text.txtpnamed_pipe€è€è¤s€W|Ðs€W|Ðu€`Wž½€€_€ Y€ÏKRdempty_subdir€è€èíu€`W­é€ls€W}Åu€`W¦|€*€€_€ "€Ï+Rzzz€W¥Yþ€ ½À0:1nN/A€€Ñ-€CÀTdar-2.7.15/src/check/Old_format/archive_01.1.dar0000644000175000017500000000273614636066467016031 00000000000000{8635153291TN01z-c archive_01 -R A -z xÚóHÍÉÉWÏ/JQä ÉÈ,V¢D…‚œÄÌ<…´ÌœT…òÌ’ŒüÒ…ŒÄ¢…œÌ¼l.ÆÄ°xÚóHÍÉÉWÏ/JQä ÉÈ,V¢D…‚œÄÌ<…´ÌœT…òÌ’ …âԲԢąŒÄ¢…œÌ¼ìb.8ÝxÚíÁ1 õOm_ >ðxÚóHÍÉÉWÏ/JQä ÉÈ,V¢D…‚œÄÌ<…´ÌœT…òÌ’ …âԲԢąŒÄ¢…œÌ¼ìb.8ÝxÚK)ÊÏ/a€€8‘V“˜™§P’ZQ¢WRQÂÀü‚ù㞆pùšù ¢¤ÈDH§¤d¥&—äUÆA”½ª¨= &rŠ+ss2ó²!ÿAbkÀ„žž>š )©¹%•ñÅ¥I@1 ª*ÈKÌMM‰/È,H…H.¹ã˜@r¬‚šsAÖÕ¬¹ÔD„çä§C¤·5„Ëe¤7„s-¼^…ä C„Ýõú B/­¸ ±¨85>-3Éîz°dƒ$È&cúµŽ“x¿þÅç×=Dø5:®l6Š_Y!üºɯU·¶¶€áÀdar-2.7.15/src/check/Old_format/archive_10-1_crypto_bf_test.1.dar0000644000175000017500000012026114636066467021267 00000000000000{î°W`3wTT€€ î°W`3w0:1zN/A0b€/¦ùÙðix`²%;ØpìG€ @1€t%qH^f&êb ÝŽrÒ%àçd¹9ÖV·Ã¡â²¦¦¥Ÿœ߯âRÜc)"0YåQu†iû:·^³ËpuÒ¯U“¾ŠJÛ]¤=qÿUÈ­8{õzêñ ‰FçC9>€cÓh˜É©·m¿oEgDÆQcYaFqŸƒyûl~Ⱥ3 ñéÆÁ*ÕÌ‹ÜpÎ9s÷Š+Ì‘ÌÄÍÈņp­.¸•ÕÌ¥óå¶žFwu±Ë7nL|Àp:E}ñÌš —dv0ûbAĨ$r7\%ý95ˆ9¢OØ8cfÚÒB† ‰©•PŸÀa-O·¦ÞSèù€½jÌõ$rÃws ¿ý¡\" ON÷“ã dÝËs]ÖXA×iO²)—´´ª•í³É9K?³gT?dŽ%Ŭé’F{Ÿ Œèy®X}=ÅBžL`5ôÕš“÷¾—„bÏ:÷2ØÒ BÌDÓ‰ËbOM7t| DÆçjáØ˜§2¦¨?… Qä®îćþŸJ£aHiöÒ$•ó2pD‘‡‡Mø}×ÝçJÍ%]˜A‚Û¯C*\,ÒU29‰Ë”q=8nQõǂߚïÙ¬f· IdVÛ±÷Ûë‚pH/ÉTJWrÄ|™13~<‡Èn1ð›+¤¼ÎRjõÀy¶PÃßhM Œf÷8hÔСŋB­o‡>ÅóŸ3Õ°û§¿æ4 gÞ`ªyDþyè{ez%Ç´¡•B|–Ds÷ÈãÆGÆ6Þ(±.—ºÝ{Ò½ŒîPš»Ï6Éòã”ïÉ9/+Ôµv¼YýªÏ·ájf¸kpž4?þí*’=ͺÏç5à-¢‰® À×U‚º5Ýy.1@J+»ŒÑzaɞDZ0ˆ%K€"ÓN¶/)NUwzÞh—UÐ ‹N[bù9Eú3O­H(Á­x :pVù£Úÿàmœf<"ºk&ͳMÐÓÄ46iMX÷«gúqôy² „ÅË._À67 T}ò¿Ì ½aŒxR$ Úî×\Ž.ô+²Dà— Û§Œ #Þ™ _ëfÍß®tÆQ£ID½ªàõ±TCž¡DLäÿ@¢÷x¢áë”þeB'¼~'¶GuÜÔu­U3ï´Kù­Tz·õ0uƒìÞ\¥‰äßC”ó¡±~ABj M¹•5ÔŠ^=ÕGlƒ*|í!)Åø“i/Ðb,}QBI?ˆ /H¾ö˜gZ¶Ã+ZWù¢ËÐ[lûØ~Ê4Ö·7 Í“=Ö€÷Ì)ªÛâ³^r>ÅÉd3#žý÷Ö}Pûí*Nÿ"{¢\Ì €!póϲ¯ã׿êܽN fAÄ"Ù¬,•uÒyÈ(oÍÏññµÆ—"óéªÞ ˆ[ŠÑ:WI%—{Ȧíòd›½Ö…ǂޢž9ô;FÕshì*1¼âz±W²­3•Ï däî ÎÐ_W:I vSb׫§CÓ•lî/2uÁÀÔ­þ™&Ýö?àÅO¤³UúÐ$XS7ƒº‰`›Y.äã0†+Mj×7w/ªÜw…RùÜz7+†_ BßÞKG \-”lµH”ôæJ“ÁpPô¦Ð÷ä2«ÑGÔ.|@É °å‰eJJ\1½‘´[{yñ»¬Ì™b‘o@)×ì±¢ÊÅ!ÊB}‚Vðq]þ5é†q?0P´§ˆØTr*»›ÂöÁêÏÇž*ZþwÄ]¥d„Àp·­³»ÒšlÅß^·_¶AMl—«ïÌ×'¹l˜ªxöpÍèÔMÝŸIôI†þI˜Fç+w‹ÿÍ@ð`B$"Ç@z8ãÎEûEÞyÈÆh=íî¶.4dƒ=Ëú¢—ê¾ûÉÔž0Ú€œæÉ£…1ž:Vÿ²ÿ÷”‰a¼Ž#o:4eüÜÅ[ú¢„äp*ë+âÃ?ŒÅEÑåÄlÀ?ý[} 3i©7³<9;FlÖªx€¢=¥r‘¶·,à¬ý‹"Bê©[&ðçørÃŒõ„4_7JÍí—x#'}TúˆD¯ªLû,þ6Åq6û#!Ê}S.`À¼› ·¢¢Bg‡·é8û\Û îd8íÔì¸Fl ­{XŒîé'/‹À,_cFÑW‡ëpáaùæ[M¾ø}þØZE òLúé¡ÐÊIQ·™·#ô' ÝÍ©z†£ïý‹³¦2J“»REdr€’#Oèq%ŸÃ˜ŸDN;nkg¦—fP>:6´:K¬bh€é‹½L0å#]Ì'ÎnÐëã%  "Ñïæ™W%sïÀvl;Ž,øÓËJž–±¬¦D¯Ea]®Ý}`t³N•B>X=”À^lÑô‘:ÉTÿر•ÙÇ­²ÄiJ2M0ö—2@t®ãØYv?SÑ|ÂR£ÍO"ÂI¸lÅ¥äNE¶õb%‡Ýëx¹“«asZQhãg£èºèˆ×Ã$‘^=ðV0cüW)ÌkDÔ¡,N†*­½,Ê÷†­ä{Ã?tÜ?o ¥eØ5´)K§ vQsƒªŸJ ¼Y'ôàÃÃà`4aXâ½èqÁhA£–†ô6k;ÑÓý˜dNvU/¹Å;FëOz£Xÿôúß—<·Þ3ÐI/BZÔ?ÙmíÕ@i£¼íX´ ElWäÑ fÏÄ••q @£ÓŽ„ÀÇK¼ðßð‡!@ÂÄs+[M`@2 c(V‰¤at‹:"Ú0£g¬f®}”|·°ð(ãˆ[€Öó‘¯3"Ʉ׈mIÌ–k­F¤öò"(Ò­8ö 7y`ò^zWÎ_…Fu‡Ð^UÙ%% «|›@Õw¼™×~ èø¶~ß ìžÑëÌÔ|9Ô!Þ‰×ÒÖD¥Ë;åÚ IlUš±Ú,;N5XoyÔß™Œ­µðd‹·†&baŸBk¹¯bª“¸ä èé‡CW ¥+Ozç‚Óy`i¤wÓz_x<ŠŽpèP`wšDe42×1,=ób½x¨œjìÔ÷{£²i\]ŸgØ ¦Úöà»:u\%Ç3Jþ,jðôÇð&3æBœ:FÿÆßÀú¶òßߟ ’ß#‰/âûJþ´­FEJÈ'Vlðý«2r¡Ç3ïžè¢ÐÝê ½òìð>=¢.0¯ˆKZˤÊ*‚Td¸åhdÃ|ÁR)”¹ˆPHþ+p0dú ·Å‚~‡¿[£ xm±ד5õ¸hа[|q_Ë¿.cŸÖ×Gkh’D#”‡;s´a·¹ª&³<ä*Û®ë!îú6cXµ¬ÿÚ6 o}tSÖÊSšLN ì<1{&æ…îëk¾Õ2}MíÞA1*þFþè°þz¥'c$Ão‹m‘gUÈ삈0ÊÓÃv>Ûp?…i¾M“¼ÈJ#P´ež<$=lb˜£êù|úù^ ÌÇ{Ný¬nVùÏß¾±CFzî,-õ$X“˸#ZwcÏʒѼ¨P6Ú Š4£5ˆd á­¶ˆ„ÉÌ…&òå÷¤Y”È$Ì+Ç[T¶N}l¼ŠÑÑdò;ðW$A~Ê´mˆPé\È=!¯X­üÐÔË:œ­Rýd@§avXuÍ¢.oѨõª$8h]Am›W¡Áê Œ=«{ÞOš¦g}è¾ WãöT¨NM1ßM¹Ó6 µ¾h¢Þ ë¹þh©>|’3¹øSê8·åT½5ÙÚÀ@¼ÔøÞüü+òÙ^õSä±R!‡KSþ“ÿpdû@YÔï!±ú­F«ü—F„,¡•ñŸ@eérѤîhÍ  ýprw*Ú"X\2üÈòd¤Æ¡%ûó»[cJñ¿•‡èYC¿¶“Ið|ŰZdÀtP«¦Ã€_ªI¾±K?ëFÉnG”ÅÝĉôùªR¼QaÞ°œ4èÝ-bxÖjM œËõ–¤¹³ÞÊ* çËX.$Œ Ä‚f$h¸ëÀ<ë¾-ÆÝ›”O×ü\ÞÐGëQÂZæ §{ 4Øq:µGÂK…tW‡"‰ùÏ ÞÔ(§„c'îa«W5ƒ„Dîö2#(â8ûD£:º6ëªéY{³…²:gÀ|wÔQª¬É5ýî®ûN>Øë$¡Ï>ò¼pfÔlñCKàn,HÓÀžÈâ²¼šÚ¼•¶ž«”2çª*wÍï…Ï,Èͥ߫bxùÒ š¦<{Ðó@c9ô#C,:ÏßÕ]ýpÎ÷ˆF*”ƒ1,µæúósö´úè—9ÐòCéf™;©¤éS@}5»¾úÞÃvî{} ðì$û"ŸmÍ}§ ðˆø +ÇÑTä!à™Ÿòè«…uJüÉç #%*¤&@S€+ÐnÄ YÊ)¶¥Ø\NñHÇôx¦³–òb1 ýÚ@VÀŠT¼ /S£4åÝ‹Á¿¥¦ß°Îļn:×´%ùívívså:/ësE6ØmöÄZBà¢#yÔ¹@j­iC*,¢¥Â¹{Â_jwMú‰Vwªk/­7¢góÔ¨ÿ®eÆwÓÖ0²×¢–{„C¦ñ“ÀÎ .è§gƒ÷“‡°HR_ò!¥f7a _5Þ²“ÂúÖ—:öë¸= Çt§±~œUËvéŠ_UæZr&^ÙônomˆæPBaɤqc“ÁÚå‘fÖ¼Gµ'ÜÀ_.Oi*XÞR)q±Y[c0Ý‘Ý5ñí{KCÁG‰ ׃E ŽGš˜=-w3¬$²b,üÚ²ôûY?w½¿´ÚëÃP2ý4#¦ƒ*“?|Ý^ðBtÞW<,jñ¡ww®ñ;¼]uŸ{;Âþ«v g íÞvh6ÚF æD`â.jPΆ•¡}¸)M3Ù@; ‰¯Á²­2ÆåGÔÁ{–ƒ\óüâéãî“ñ0íªir®[æ ;HQ?4XH@óQŒèGó•²èÖFSF¨À2Ìõm :¯.’ãÕ©¿[–¬Yý Ïá}sÐWî%2?böbþt¦Ù_§|´T8wKmJs¶n‰ï]ƒ,Î5>q­ãÀ^tÆ€ï¾ï!ãé–€çg;Óhô¸@¤AòôˆÆüârnùQÍ~ãݾwB€ÎñÉ[CÓx!S Mm ±³Ö棔‡8Ù¹ÖÆuÒ›†±§ÊÎE8üDX.5¢ÆJ©bn'Ï.Ìë¢B»Þˆ'8´uZ×9ݯå£óä ÿq zvŠ©E¬iÞÆŽPõ|bFÑ£ŠBmösóŸÆFS ºÓ^)‹ûAût§nyY€Õ:@™PûˆV¿$,Þ¼¯0?¶Ih0FÛéSž\RËóPû«×š(¿q™ek¸Ý¥Os‰…að6+].¿û¹‹Þ©Ä8[DùAÅ“,b„CT¢~º ýæ²Â!ññÜÝŽõ<BÇÑÕ_Kóæ{Eý8Üxq“LgkÌŠñD1ã˜+"[3HͰlþ ä\šñðŒŒSö73ªïµIð!rèûÕ ø%¿î•eîíùÄ?¦Jß³wö9 ‡Ñu䨛&2üÚÍÞ0Ïé©q²»µßt²#ˆ€úÄ+€Ê–¦‹(ŠÞ‡{…TûÄŠ>ìYÕIMY¨»Hî4çMo$†ç™ßYåÈð+¯d7e@÷©rGȱCwXî ¹zPU£½¹ñuj¥ ]÷õØ·{w'ïâF\l†¶~…îÌà“¯pVÀùAïJOî?Ž­ñ&{.¾Òïî6è®SÅhØ O³,Rzÿeˆ”hãÿÕÔ¦ 6­{=¤\Ïþßß…Î'—¸—H0\º-™e(Æ¥ÖŠ3‹4Ç)hµÌnofú³X2„V¥ê*®›”»¸²n8¦ÂF9O—”·¿™½Œ±èÙô©t`ÍPsöï žØ~Öß¶ƒ?Ž…ºBêjžŸõÿ 9$Gdû™tyÏ(пr(äEh´®-OÜ<:.7q’ß`z.J5!ÍÙáïÂãÙ|.,¹9AÖó!k-ÞtÑ0Õ¦RëCúŸÚ@¥®EÒOjð~‘YÁb«`Ñ}Q]ÿ“°ßS`%^a©£€>ÿr‰9[à!êмH{œœô ’ù€cêYðG r>+÷Wªßt>¢Ýõ.ÚðZ §« $l},!–êÃO÷U´À´ Ø?7T8˜F8Џu&)séŒ:žÆ¼˜ÜÕ¼f«èšü!žÏÿ4› ò°¿zL!h0CakJ Q×.cœÆ×Ëé‰é–¦×Ú–²lÆ<_½ã¤Õ¨_ø+±*Œ–Ä.&<þvŒqa͇¹*n¯‹ÔLTVx¦x]cæH ¦wU²‘*Aýoüšý "B2¿RË›]`s®¿í[ŠL±G[ü)F–„˜ýÃ쉽³ ÚT1f ‹&™šØOui1êˆ0>–=œLjÍ(FÎÂn‰·d9‹k$<×¾Õ¶.ôâ%¥Eö{ˆ?O1]VžTCVãPÒB…‡Ý‘-ð;±3Å´†ÀG#À%Á·{Œ`Ó6Ðe©Â¨ó!Ñ"Ë3ŒT©ðŸÇý™J죥á¸O%.£î€f¯o¸¶9žã2]µÇ_ b8ʃ¥ÄŠáDÑØÈDÿf€íËt?fíã-2é0ô<âO¶Êb£ç“‘ŽBSÆÈ!“ Ï(M>‡Bù¦ù_®ºƒ—N{ÉX׉Y°¢Zšê!,ÑŠœíVbšÚïœd €dOüµwzû>5S·wáò2GF‹ ›Ýáaæ ØƒÉXUaÊô‘ãžñ-ìèFžs¿@Áß(Û.h”ÿF2vöÏA™e<ÄZ' Ö(&<IH ¦÷k™`äÿ_ÅéÃŽ¶Y:xã©xB‚ºJ‚îg^|e©&ðOîû2¬©~öãÝ©ø(4 ¯$԰϶Pá7ö¿Gz-D)dj¾`Ž) £üŠÌÅ:úZˆ"ƒ²Èz[%­˜•x¡yl êµèšùëK€6ð GwÔ¬d. Š• ±†étWφw®êEíLN¶cÖj‚«¹n©.Wtö„³cÛýóúýZUsuu¬?˜Åê¼’ð“Ã<{ílz ž[=díÕ5[µØ6ëÓåÐh¤ÎJžtž¥¨…øH¯Ç˜ÓÄþ0(U ºí Z´ìŽÐs#¦¦™ ‘ìJ2ûlíÂñ+ñê¥Åêæ_1ˆiE@wªÉ{*9ViVGÖ¾& ûKHõ¶{¼eܸ þ"Ùhמ Û–åqnçG›A/ˆd#&祲wXšùýŽ´‹a´b³g{šÚ8”…'ê࿊cÅ=Rám莌¤¢r˜wÚlôˆ%ØŽsè½ÅHÔú‰l­{Ù³qÇM!OàÕ‰&[Ž 1i„w%£ ý7v5 —«ò¨Tã» 7õÇ*×ôʪ7„G^P†ûÞ}}9õÂü×+û^Äã^|Ú@º|33º4rÛpGÝ'y2dî@î´®À«Xب°y/^öÍSn½ ‘O¹dyµ0÷cwƒU?´tZÒ+$gÉW‚{Â_´Xf!cè| û,QB·¬ú¦¸X‹ý2L¶dK!‡ùÄm¸;ãl ŒHÆäÃÄV£ÑÇ|»¾´Ž²â™(ʞ̜¼¦__Í%ßÉIÓ®ÌÕrJoÏË®LLh›âœ¯›tÚÊ­É,µÉj¿Ö™IÏÙœæO%1%⥎_³öó± rÃÇÃ-­ó'P¾)ÕüK¦%‡C²=¦§w¡B+OÒëñ{J†B”-ˆ»kê¤ÚPs‡–øÀ¿mƒÂ$“;g’~u³EÏË@¤FFª¶=Kün!å¥ö,»µó2o­6Q¬Qx÷ÆÛÖcP='¹Tÿs0Œ–ÅÓãz÷”ð¦ˆÆ™: TKœ²V¾ós²:®j3±ÊQƒ›®É‡±¼×iûV{½XjŽ3—,tìßļ† kžq'Ž °}M<¤c÷Ó¶Æ]æ——¯¦Zн˜ÎX÷š¶ø¾@‡jƒ²˜¼®bË÷0~Í?]U F’ä5w§iHìþúã©ñ$$H¼ò§*oÄ/»ñHåYœD@u[†¨p. ˆ4^ÝŒä1ú ¿Ìû0@\ A—g´éÚÐþ¦o·’˜Ÿ+e…fOúhPM–ë ëÀLÃ8¶q­9؈Œ±û8}êl'E„…¸Ii½áÍÖÚy4· [³¨Äb)ÔµL„˜‰eûËGÊl[}ª,âJü,¿´ø=]fÎJ]wœSHüco-ºK$M(¦Ì¹a&_ÕÖ•À`؈蔟þœltß?|¶‰ªÇWKA@(.Ü­+bâÇò(†u…àž´¹g¹kÛÅà °šãuäa +ìÕµ ªiCì|›‰~õÂì®NË‚­EÓ¥wB²ü}IvhžÓg•ÐYé jŸ0xY*¡Þ"ÆÄ;³ðÛCVz6ÆÚøø,ÞÓo ‹þ×.ã”;æþP>¡6ìkãYñpUG{¼`˜pk×¥–CÙÑ×KM˜=+ë8¹µŸ k@ªìÃ?bˆÚ-ôg&(=A¡°¢†brÝ=ƒ‹qá¶.¼0‡káQÀÄHO2‰ É*¹×ŒËZ`ËhÔ4šqÆ@z(¹!eÆ0÷ÖeDë3¦°Ð‰š|Ó–þ¿¬ýÈÜF2^£«¯”Fi0™[‰‹ÀuÃ[½\k©€u#T›’YÙD ‡ã™L&ý·$Ò·ƒ†Äaç£`ûŽèˆ©œå÷Õß”ç‹,*ÔmAÏ:)Ù„‰Ìðù¾wjØQ–Adµí®ª”/[ÀìÑBç¢IJD~­úŽBˆÔKŸ´ëí¾Ú[ƒ‰++½Ÿ­Š3Èó¦X|¬Sý1r29|•Ÿ6¸Ó`"¬©šyq–ã[–7œÒ¾¤Âˆþý“È%ÈC¤¾ÖLfn[’Ã(€¢àÕ¨V$ÈE÷|Wb¥o²Íd mÊØ²ºÉ=Þò>Ðê/Úí_Ëô÷ ›ÕÆ=0vFÞ5#½Ì|l0°ßù¯eðBÕèÕ§"‹apª-¬í¯ÒÄ~(ûÔ«œlûºÂÿh‰6¥cáAïö»»ëpÜûÆ‚(D©š§¹jNsZ\2щØÄû‰¢’„fè¶Ë¤p}è©ZdüÞzZ «W9fT8i é>ã„>“-Ù¸ì ÌCYØYÕf)ènú «šõX’nÐ`ÿÃM½¾=D`ÏìrÊÉôÇ>€yÏœ!à¡Kc¨í/æ“o W êC‘×}ÕÝ[œ)Äz_í÷.0æ¾9‰ŠÅsà‡\?ÁŸ½IùAB·]2××"ýK™[$ðÞë­}§A¤iNsz £ulG‰ m-êín OÈG3 —ÃàȤ_”ÅùzèC. Úù­=uy¸ÕµÆIŽüàqÁíý7à‡¦ C½g…!£Òyóo&%|åÛÚ4j3ÒgºI ­úRÏÁ¶ÓÁcFn^¹[n‡1VQCƒÔÓz—” ±M=L\øGZ™…B!rÀíDLJ÷êYfJZ¨Ý¿$0…l±:?&ëAæÿ#±‘‡sÒÁ“®¬15mû<¥J.‡?åLÙº 'mî_>à/‹H྾GT|–)>{û‘úÈ)Œ¹Âš1VÀ4A•=ÖS6"2)ä]×Þõ-6Éäš á—ˆX ºp]·s(¡",`WÆÙ@/œìocwÒƒŒa¸ Àäæ­cdh8À˜v´ðiõ”f¸ÞVÈ—Ó¨@!m³ƒI…¥!)®»kn¤ „m딊½¿ ¿áÖ½‰SÁ½hPt[ºžÛøM èl5.Ù¸¢ÁÑ0¿Å:Þêèzs ÷ÁêªI?ßoŸ’å¶hàYÀ@êù¶Œ<Ô–·¶¨˜é#Z/ö_ä ‚?Ve¥Ä®x3¸¬¼¢'‰/ú9îJ¯^Ùpý_­Ì0AœÝ[–]·ì¯µÂ·r›éÓ±ºÞ/—Äü¥U•îgÍ?ƒzÙü#¿;}×D,žBY4W4„œ1ÔICt©)À =&£x/Lm6߸¿^nrë,~ÂÉUPNéA´è-¥ÁŠ9íyvXîçrÚ!¡ž<Ò.î6¹û¿¤}Ö_ìMÚ°ßm>:ÎôɦG0Œ}t¸xBr0ü#འð¡\3öÆQ^ý‡ðçCÓ5o/K[~,*ôw)cœÎý{ýÐ×VžÙŒÞi}cÏõj lúc[ûÌ‚V‘ 1W¦4B]œ²þåÉÊ—P±œ¢ãÞÿ|+ýc%•òtÑrQv)¨>²XtÛc©´øÆ%p±†t[÷‹§7¢ G!¹÷FXri•#X=ZÆuÚlÉrà¿ÈDNTÁD ^ í8Œ£Ænîè”Ç&+»ÑrM.w•°F°hþ¯ãg”BȨ`ó-†sfy¡?œ·A­˜ãp„î®ÒS¥­b$žž®Ž'©½RÈ^X.) “˜¸E&Hr¨²°€³zV¢Ìx<É 4÷Æ VÔ_pýjȈÀ¤_®_¾Sk!ª£ÝáÄñ-.¿V½–*{uôêÒ°_ì—þñ™}Z"kÛ‹›¬É¦~/n‘²§ß¥~u}>òÙ%ëÏ µoIHi¿•Í ¾hûtcûzœÌÁãþ}O@¶9u†Ÿ%!}ÈðÜŽÔÍÅRHXE@¯†š±ØÕ÷²è!îØÔÎMñDÐý­«øô‰]PIÎx§¾p}an»%Á(¡a·±çr¤ˆó¹C.Ž.7Õ ¸ê×yGЩÐq‰ìmùª1îªP«Œí ·OLa >*MM+rÖsŸ×e¯i¥¡LM+ý®”j¸üν*Ñ^É:Í%~QÑ` SÑ=¥v‰ãäê5s,ϬóßbÌ<¾„kÅPX1=§ Ñ’´]¥è¤TCŸƒÕPË‹õÌ„l‘¼çpÜÙÎæqÍð_½K¾ä·ô.ù§ŽWý©LØÇ–]\Ë¡ŽÔ˜FH¼«Î»ÅÚˆ+ÝXÇdÅ‚ðÛtÃDû íed_þÅ´¢þF:vä‚uøšLŒ=ÏeŸVwëCÝß‚l=Ô€’o “!Yê÷0-ñGatYZ(>—i—g‰5UË¥taexq$‰Iãe]×ҧͪ¦P–”¶ÀG<À½Eû ú.Î"áÉu´ §DдÞdÛ‚¿ÉyaÞH*~ò  ‡*ÎI@ÛÁ/)ô¬µý»ºcä‹Ä™D³ýï7´EˆHùõ§†z(Ä ŽÍ7žwílsB ðšš»š.f;ûq ÕÄyÊxáo«E℘v²ø³ø­ òÍDqæ­z*{žô.—wûªœ‹é™èŒT·º.ï䔋5(Ž i–aŸ¹ŽuF`ñÑ,øT·ŸYÄfIrB8+×±¶¾€.õY›êejÅBö¥Jî.åŒ?¼I—‹•ĈÛ”äø¤;)öJ-ÍqésQ« ìäG–{ólÁÆñ©Ú(§Ö¾+*tm9Mèfð I餯° üÄïv·Aäk½He1‡f*¹Hÿñ«F†ïÂýüšµÊþ‚©sÙÎw·ãÉÇ~ ®E#©ê‡ø ¤‘eüªzÕÝ»äãé±|Cw°¥éž\NiÖlÉ1ªKŠ–Ÿ†Û ¿Ä ·ú4pi Ú(„´TےàýzžW¾Ê³XRW½y<è;‘'ñ–Åpéþw­K((ŽjëŸÈ·8׈¸ŸçÚGY³Tzû¦ í:Õ{8’ͳ×Άöï¾Lß ¢LÔ÷i•ƒˆµw¦k®œüþ€â?Ò¬ Þº(aŠhŒ?íÂÄú#ܽk¶å¼!²„ç¶7zéþkõ»€eÊ~~×Åûʱª9ž¼áþ¼yâ€{tK;D„røêZ¾;„"Ph‘SŒñ˜G®0ÍjõÊŒ] )›„‚Tz–ÇóJ?[±£Rƒ¡¦ú†j_fÛäÞôN±ÆðÒF8ÓœÚÛAmVGê‚¿á.¨šA òÿO"YZR!àì1ãHþGø‰xzTzcë˜ÂÔ¡PKFõA]_7‹»cšÆK½Þ×,q Ɇ,Åòu{Š_%ç«X’í[´9XÓøüçÚœŒSª|QÜ:ûca-9©6¯jmpÑb¡>ûäì]©¦uä:Þ|¼§n’®nã©öðদ¨NÝÔ--¹¯eP`ırŸÉºÕ©rÉ“,f2ð$KkmQ*Ý_Ú]×l‡¨¡Í/.¯ŸNkM£ï?xó¼[Ñrùóô.>}8 äS”%izÇTÁ$Þúì¸ßb%öwS5 6ÿŒ‚‚*æŒõ1ílJ eS.÷ËÑs ËL1®b w.³åˆ…GiLõß¿ÿ®›·,ÄVš#~ƒðÊ]˜:öî‹ðŽ(·Û E-WÅÆªI¸óŒÙó©L`M7Øòˆã¢®Ý‰?õ•ªY‡Âï¬ÔO ®ïvªeNÏÿ‘Å\WØÄ)î»/«…–]·‡à¤—.ªH˜êÆY^)Å“\¦ÛÚ˜Þ6îK´YKúF[=J°ßÀ KÕÔ )‘µ ãs¢ªä_9hÙ ÝÛíÙŽ¿ÆÇG*º¿ `µ±5œø<ó:=š([Ë F°·Q;¤Á!>j7¬ûš€Ãc¾¬`Ï´Ðwšvæ¾útïÑÛ°ÜcW<ʰ±ÒjÒGMÚ†ê¶/¾¤T4ºe¹œãq4,èëmdÌC7e}#Å®º“ «ÿÅî7P úurw°/ן¯%NÞ½þ½ Å&` ‰7‹anì:Íe!y÷Ìu4æîƒ2ýƒãôbú4U9LBÛÜà„™Ò‹I¶5}6ø²[ØäL Ý;¶Õ£L¹]‰ ¥„Ú~ˆøÐfúuüÆÅüiy ™pë³fåŒHšÐ›‹É’ůxø-3lŸ’ö%Ö#ÒßtÅ ÉÂn£<,YÖh$µ¦ùw›qfþùQ‡d™+2µt·Í’ˆ†Ü'¾.7ÐávïÕ)$%—Ñp4÷QΆ¢ŽúÖ5ëÐæÝ}§LJµn®9•“ÖêË›rPb #×'\í@T¼ ›MMßÖ2¨ÚÇÁÉR~ç³ÿ†³Aè¹y»fÈ…‘= …,pªjjú”à:¼ã²»/—æxqyveXvdsºÛù™ÇËãx?ú•NÅEׯdä§áÑîü[f¹CŒÍkr˜ÝgM¨EÒNãÄt5ÁëË|,(õ¡k ÐRwJ±K–jH ›þHeÇ|ì<ññ‰ã1[ñVEÔ¡ƒJ«/”Ÿ–I¢øˆT ¦yÔõU^†pWž¬\+R|ÌŸ}¼"$µ„½QMX)¥p|>k…ã²祔BýSŸàÌ)Pa’/ÉE`Äð5CÈ9óΈR¡ã÷˜Ö1C*>¿çpoloíÛ'›d3é¶ø]Ì-b÷ï3²¼Õ:m¤§5]T—™HçÙ¾‘P>…æOíÔÆü‘˜ È™Zðnu[¬ó—ÔÖN¡ÏlV<ŠíspÍ0þ·JíNKJ©n’Ón ¯ wB›ß;3öš"Ñá ân&i½¤»…œ&ójX“½µPkI·úÆûD«¼ÉÛ*D½ö¨|{øÊD†~µ1˜>¸Aâ›%º…*=ç¡›^Bá6m™üI­_M,ÙKfeìïßwÐuÛ ›FÂ`Q±¤Ê<: ˆãÝSùÁ«d¨iVˆÒؾ~-èï@l’2Š]ŸÏ}"³Êaf¾ä©jÂoK,0C½%ȤA³“ö[¨Ø^{WFüØ õÇ{D9ÛèÖ1¼é s¨ºÏ«ðœ,ñ@D,pµ­]×^„¬¾\k‘QTY„¼»^¶KKèüRºwò:› ÏùBø6Ž›HV•òFxr9ŠE\ãò4²S¤Æ§^WÇ¢6ˆÔxŠø» L§v…P6ÛZйû[`gáQ³;à y£^£æù–/ÄZºèÁ˜zZíïUeK±à«!‰î>¶ïS²Ò/s þÅÈÆr¬Ò™î }>/µÝ÷7~yç¡ êvôçá4ž2h[,;Åq‚šèÕL§¶Œ¢a°~®Š@ÜŒ#c}çV¡<1éÔãþ˜CÎƒÖ ÒÊEÔ4DÓ¡ø“Ã7æ¤ÁÄÞ¹ì‹Nh¤Yg½8^vÒ-ð¾o™8ßjè÷͇P·Áî»qìÛƒl2&èD¹LôòA®yBì‹Å ÕV0Z‚«=-ßÀyØÛf®tµaÝhµpöÝM~Ö%yd_ÖuÀpwp)]ÄØh¸´Ã €ø¦ ŸÅMçz·Ž‰ÓÊ•±!¦rk‘£‘*êÆŸQ§1§(œyKÑ}4Oè÷,°îœœÒîÉå :Ù‚nŒÿþ2)_Àªa¿ô GÈJ_ÏÍ›9É9Ú×ŠÖØ„'$òÖ¸Ù#ޏÁ2¯‚Èÿù®|a‰ñȦ+СL¾"uyÝ‚€·¹pS:þ<)C¼0Š\WÔ¥—èÇÐð*`ÎÈ¥½éd`e2éz„s3}0^Tñ†ó’˜ª## ·þ§@b¤Š–Œ0ÍØWÞÈ•~»„¡/†´²!'¬g 133 ßxsQ +Îà p¹/”’¾bÑ%ù…é7’ë^`ë ’x:e:{rZÌæèÝ_ÜÀÀ ­ã·Á=-<<rÜŠ oÏG2dEyÊ;¿ ¿\²Ö|ðSÆ*ù*æz.RéZ¸P\RWÂ=†;™Êò:x‚0öð“&ÌEý>[}žî²Kf"GæjP&øËqQÈ[s¥W’NápLìÞnæ¦' ÀÊþ®Ñ´ï†C‡ñŸd7¶éŒ˜æXË®>K(r±ç…«$ÊäZ’BQO¦Îùfƒ}ÑN¤^7=:Æ7híYÔXñæî6<¸ËèÜ` xv0-‘&èº6Ÿ¦Ëâ¥Mt¿q˜›Îù%GÌü£þ³miSV:$‡að*˜á°‡ Õ ‘¯œW Ro—/‘¤ŒÅ ÒIëlß6ß:K¤èÛí¦˜ëö·iV@¼–ù¯r"#Žþ¾ ä pã)£~^K«Á,— “vyÐÓÆ>Eì>êFêUðéËܺŸ…¥GÑ’¯ƒ¼xZ6¤ñr¦+Äw©õ¦ðž›q°SOÞªB‹kâïöƒ»¦ËêQXÂeG#‚aõyêÜ2‹i7P¸.¹>¬ÙÙÈÓSÞöÜ<$€»­A­W·ëW€²ë1ÿ¼¢KÁ—ETµÃ¿ÿ‹§)F†z^nz Äö qÈ{)RŠ>LøÜœÑÇ ?˜îõ/¸å¤Ë'…'Û3UC öQ†¡ ¬#X¥âïè~ø ¶ÓC³X‘ºòäUŸÛ äØk­JK^€ hÈðb†îw@ó ‰@0±ÜY²©„Þ5´GyË­¹é·Á,1p!®PÿÖU$tó"îÝ5Í­¶(Ûtp`yÐÝ—QGqÎtb\ùó· ×tªÉCä,ŸóÌ,°Y;¥] ,zÖ± s­ññ=®ö TV·û·` úñÍËÉn¸H™üuOSP)Úâ'–“_/Ÿ´ ”ðº¥LÌØU%©‡™á M1°\‹ú²£ uŸJ ® Dšõøó gùî‡êÓAúýÍ6 !®_±í‡Cokôã$§¬oœh½ÎzÛNщ¼î}‘{¦G½O›ÇvæÇÅíÐ î 7¶±0Cp~HÄÀWóˆ‰A)ª®¬«þ·$?q(oÅÐ;•§|]Œ&í‘èf>‡ø­7;Š'ÓQ'•ˆRJÄ· Ææ)*ðpgaÇ}î§±ŽÕñ•°ÜU"$Sh¨â8µ>‘£¹j.²dßo`=/÷ŒÀÃ-4õh,7{ºë©èÐ h<§¹Ä Íß2?O˜"Ót»Œy.®S«bÓÒG¬ÍøèD–Su¢îm,HÒ(”´êl#/[Å)—ìgbT%2Ÿ0Ì® æS'Óºº1üŠ0_ļ~mT:D:K\7dÀ¼êïÄÓË\Íú9í÷øÚ/ú–Õ½+ñÚá„xYè×°yÙÂV @6 !Äÿ[6¶³ú‰ºt?AÈ0Ÿ,¥¬ÉwRÃ] {°Hæ Hžù«Sêqñ4ïǦªÆ½Cy—}VÀ0RÒüUh€5Øá{¯"l‡^áÙŠv‹\‘øÛ…ë!!:^÷ÏY+&æsu OiàòUDvÕo£Á˜µ¦ÎÛ  !ÍWÀ¤iXÝ*ÑÅG…o,Éêîñ•’ãiï…4ÁR·¯.úɬ"¼’‰áz |w‰:»Û óùãeù qQÕVøÊäB(·•ùÜúúôÿË©¼z’üyªzÉ €±ZĶ@{,hõ—Ã!£Kò2ucMÇŸÌä–~œËë ¨O'Ë“‰öèÈĬ¸#ò¶GL™‰Z95v|w7[Wå†%Žý²ok•Q`ò!ñ…DîQÞ®*,¤…¥Ós¡)øsߦõ§ýÑ5D¢HY>¸Z:g3ö$Ó5@¹ÚÝA¨¯Àæ:Ë-hn·#ÔÄTSIã6Ö— ÄÔ'Hèºë›qëå>Ë4Ò…¢`£©T¡“!Ì‚ù;Э7v+ãG&$\OèóÊ[E»TÔ+)¡ßÅ2T±}äó„%z|ì¯à¿O^Y¥ãö ×¶µ[ ¥&˜!,¢* \,ÝÊ‘bD©Ù©æ[ÎDRXªníüõ]@Aˆ³SpꄳCÙ"¢btpäQìòÓ•ÆsVA»»¥Ô‡¿üߘÝe&æÓ%+›Ðñÿ¶ú•³©mD‰}^_Ú¹À»Àઙ+2p>‚T‹+ËjÌÝ|YËx4Èó¡fÇ9(«P§TªñßÛÒÎ;¥ª\̦7 v¢÷„ ØÍUMüËÊT²NUò‚®dÏÞÛæ˜0ägÉiL,O`ªPÿ}¿M¢?†û2;cY'ç*L­°iSNª>‰N«wNàâ7ó&÷Ògö e¦KÚ.¼£qy˜¿k1I¯nRQNPkœKNÝ+ê/ ©KÒ–H°Ñ¿Ãdž^Sþu‡~KÿqšL ~>ñ¨pÆ +m1ÿ•Þåëx–÷e!¯—6¿ŸýêÌïC­xºj]F0 µE7_n¨ˆ&6Gþæ}Þ¢SlãoÔ‰h×NfV|š²áJ,9NO§„{‘%£Ïž3¨sõ6®£¹åæqìtw4qÂn%ã`¸å '–m—mkÜ2û¾[{šüøÏç´ò¨AQ§~×Q¸S¾ˆ™c¹/„»åp:§æb©¤œk":Ñ›«´=0ëün ¥Ï0ü]üÔ¬Nò5H)^îxµ“Øbt#¼wÉù£›!$8ì]&¡Š4·uŸ÷`Z}öMµóðvÕ̓\‰­?£^:ÉmùLà ¡õmGbO.“O³Ú¥Áb글^ÓZ—@Á2 Ù äÚ†ßD(“r({±@ó;6Å 1§î€f2tÍHVòÌdÃŽ?šò͸§Å›·÷ŒŸä•Í0ƒÙs#-Œ÷:D¯º y  {¼A'¢%]ÈøêÓD´ëƒ–!¸ÄîsGœÎ `8PŸT|Ý2àÝK 09cËyÊaeùe’^ù‰E••².G”9KLØ!Ùg¬ŠÅIõÞç)´»€þñ¿ð"©>†§}•†eæoiŸûpPW‹y+­¬£ßÈé¥Ã´wf߯f¹¡±Uu–¬éõrϱWš #3Z˜½ò å;'Ó­[šH©“Ép=ŠL4°ÒôbBd&-—qØ î·˜Ê+Ôsž¦³ôÅ`]&BAEuTA–ñ]„Ýá.ÖÈ"¼†™Í!½5Š¥s áÖùë›|ªËkÐzˆ58ø¾Ÿ¡ÂU™Y7“¯™ç0ù†a`Õ:šÿ ÙãµÔÄ0à­~+JLK®ìÚQ1fMÉ6IMÇ9‚7¼Jâ†Ö8µßL3³Kß‹¯ØúXZmÚF(ÖbÊ O90{“;<í9E0p…y@1I¸egä‚ÀÑωHÉíÒs]ëÀÀ±[ˆ_å-îçHŸ°ø5'¸!Ùl.÷Á.}´Î,™‡ `ùµX R—2ß`)vþŒÓÓ0Htk§#f¶dcJfW™3­Cl³œëÂ+Ñê³ð²Ø±0òõàëƇ9BpÅýºçÝIä̇:5ÞH€O® zA”]a1Yëë±£óFÆ#4øŒÑw¨Ié‹Ï¹ƒÕ 4œ¥Üé £ü.~"ZD0Tò ù˜Kì 3†`OCAxYÛÊ´LQ`S¦¶p;¦ÈÅYÕ¹‡©¥¼õ.BÛ蔎ìc;1|"¢ßÅúáÜOB¤äÖ=E §²óã†øäxZ'ĉ¶ÍžªñRæu¦hI£®ÎW¶²¹µéN¶Åþ¿(‡þà§ ál}„?K²ŸA©TŒÛÖô04—Aâ 0ÚÆ¿†b¿ÿëy p_¼xsàê_†éçN•ù¥V•º˜mðâ2›µØKEÙ#ûqû”†Kñ˜ŸBÕÆSQªÐžIÅ|I(©7…‚ßãüÎÏ)ü‹ÿ}£”åy|¦ðVqT‰ÁÊ×øÙƒ/ýj;ä)~ñQ&rly›_ÿu©kQžz»’F¦é>¶îøú9i¶Á€èÏæ#0º4k{VŸU[Û‡¢´+Ÿ¤]…¦µ\Ñh`™^]Pž2äTúhþ‡†‘ˆ%:}ûT£ÓìÑwËyŸò4 þ•÷ÌäWRõbPX録¾xœ¡|$khGCÔÕIþ ϰÄ2%·«XH´{ÑÞJÎÇèõ@cí…³=b×ÈEÉ@£z‚Ô ~çY›Lý÷n(0Lvžüšñž AdF6‰ª™=N™Ïx’qCïƒÒrVÝ@’gƤð9@fûB£m¦îV7ÈwøjXs{ÿõBÄÓå†g&”äGc¦åbÀZvyò¢“:¶ùïyÖÙqÁô‘nž’ ,º`¨ AФÅ® ‚QYua VcBUõ{*“d¤{zþ`7à8«õ«…&D¦]h7®“šœ]`iÝÅa ÝAÇžKûwV”ìÍ´Ü­Vg•Ú!ÆûJ 7ÃpÔÿZ<ÓkC®ø8¯éò‰è·ZÔC)Èõ“Uoö©©žs?6=î'ýýŽc•­HâùÑ2½3˜dabî0óPô®^,K–ÿW|X¼ëbæÏw`žæE²[¾ØÖcÌmzjO%{¥ŸVYZåsak:š-ÂZdî©ÅAÂA]RaèÉPÑíÄz[Ú/Jî:; # Ð-âô3ì“VÍB^Ö>jÙýlG  ,Øo(¯.;è½F§øtÝ9!¨ç —;@JÌóetaF4¬óRd…Ôä°Õ½gÀ¿eƒÀ®¯;ÅÊ è:öSç=yħ¾E¸ÕýÅ XWÐÀd|“õ¥ê†Md¬4ÞA û~¬OÅ] Ö(êðrü zdš‚ºSã$,.F¢ ¹ÎÆ1‹¯~Êìauµ(.ƒÓ¨ä—¸¡M.‹*¼¾ü8á’±GCPÏ[Âý ¾Íc¨£Ïb–e¹ Á]œ"˜Îa$bC9'a‰¨“ ½Þ3@®Ê24ç?ÞA«Ât¦h¬A7×m˜¿Öþj°ðĦãÓ÷p›V>ìj.lWt9WrÚŸŒ¾û˜î°z²¶‰r„KŒ\à;¦I¡äÀ\W¾ÑAp/u‘±CLBúQ}©•¶×2f ð:ºUSiyq¨Ã2àƒB’; µ¿S…§&Z|O±²Y^ùášò°H.¥†E÷’ a}4O@dòÈsŒU6M}°Z´MÁT(—2cF<£w„È‚ür-W>f•º½KÊV‹ÿW‡ôêüj®=¤¦O×Üà :QÎÆGíÁ¡©Kl6h@ˆP™öBl¡';¨É*¹MÛË£ŠSÊÊþ«)éÔY¥87&ÉCÏåZÚ—¦1¥´«Æöû%{¼•R¿NŒ´!éAµB¡8Šo¡S–¢gMlŒºaKm EÐEøÛ|·¢ü*Û >"êzéÿ“ý v×ÊE2É-ÂisHxôã|Do ~‚ÔãÚD„&aºx£†þÌ.ÞMã3´æaÇZ9¬!9(èÄ;Ì”wF¯î.7Æß\ #ÏúDÌ ªmH$Ø=,þ öÐÝê^Âç1~ƒ[Z},¤^ï†Ü®®€•Eag%vƽÀÞäÀôƒ?»\Ìé-w¦œ¿ÚÖh`=nÃu²ÌquHdWzlf&5IÂ'µm be®ÁB|G+Ëw#CMeË7‘~dT0²"§/\jÑœK3ù+%ÎE‚*Ú.õ ²™ªÜ­ÈTë³îп½|Šûüínqîg'MM&ØŸ{îEsqqe•¶›Lné0qG|2‹Ô7ê)¢ß¶»( ¡#–´ 3Š\œ„²Ð—£ü¡Vþ¯ïr$U-ó¬Ç"ô­?²´`d²ÜÕƒ¾˜ÔÀŒ'Ú”’åÐ=_¼ôXZý§ñ<žº}¶A!VЙ¼Šš6¸Üߺæ•„”Z Š±L¡ø'—ù¥ÓIK/jš"ÚiÑ­(†`X¡ŸH¥yÿŽRS,ŠAð³dºëñеõÅ ë·:ܶ+κ53ÁÞJR¶•`ìáZûÏ´¥DIôšæ²å©¯=¦Õ8&W „AåÓH1]®`æ}Zh4ßÓlQveÛæ¦×°·žy\‹ª¿Ä7Ö´3p|[î/ï–×9Ã¥ž$~o£ÿQ8#p­L¾)¾øhÖuèû‹>+ÊÅõ]bfçV2ùê-ë^B¥²ˆæ‹Øõ9èæ¬®Y@ÇΧ!Íÿù†µÿHµŒ\…ð$OÐIRÜóƒù…^Óg˜FN@)êó¦®`(™Âl‹€Z:ÿ˜}°ܲbÕ´±þõÖ|¥t lçõèú&G»îöªÝ›Kÿ»Âuû©ÌDCɨ èT‹Me'oÜn¢˜ÆÌ ùÙŽ W-øf Ì6ÄÍ(wàK_9ÍHoÖÍ‘cc¨rHÝ‹1¥Í‡œ'Y¹ô³°½f;®¬¼oâ¸õµTºM8ÅÚW&…­àf!¨¿”ø ¤xÓ¦×u‡ §öMð¨P ‘‰ËÀ3‰?€íóKZ<Ôå8`.øG§vRw•-'—.œ·ŸveŤ±¯?û6CøbÞÊý¹8<¥cÃáéÀ3¤¾w²éÀÿf £=å_†r‰Sö> 'ÀÛà 1©>DàÅßl×J7cHÒnŠx0¤=–OòÌðÿªÙwG×ùíÚñ¼;sŸÓj£Xýöždy´Lí<ÖxKÞëøÌð“B‚o&KDs)¢NÂ×@I!þ¯hqáØIõ‘n–zÊWïÚŸê4>Á0ço“0‰(ø]Øey-=»ò;4Yì$Ùò†U:~MÔEst¸ÃkšQÕ®UÖrpù¨e³ÙÕ0[nVB)K§ÖJsýMç[ሤõ íƒ;´aµž_'\U U«R9}€•àd#(ÕÔÁˆK‘ÕˆlŒ‡1ï+7ÈÄeõìH¡©ÊYý‹âΔ*’˜fËRâ4·ž‘fÿŽ›Z“Aü"¾5ÿY%-«7øä Wóç»Ñ†ä R‰6 úªg|áº"àÇÚÙ—(w ÖúÝNWæx¾”î•#Ãè‹Ær„hÀQ‰hê¿áwJu__GÞ¶ß4ZÓÊnïÞל’yJE[î=‚^êA†W-¤ž~_ú÷JQ±¢ì(dR:7VÝ ÓLjeë ¦‘AÆfè€rb\1p dŵ^Üÿˆh·ŒÙ¢ûáyla¤§}2á°êÄènóbšÈv¬³·E‹ÁÓ›ÚS7ˆÖ5²x6œ[m­?û˜f´%”rìºÎQ‹µ16ÎÐ{AÂú£"é}¶âîŸçÇB”±ºc§ ZÌÍ|'ò£öâ?¤y€EƒuRO¦èPÇyb+£7e´1ÊŽ×m›+ìJ@ïL¬-­¿¬BX¡Bû›ë¢ÃÕ‰† Õ¼XÁ·Ö"E¯=YWÝâ­¿ÞU֮Ɋ%‰€\ð†]²‹áå;4ÑMÓ{ç¾½Nò½T¥=ügwÕ2cÔi Ø Ž"&Û (Œ`T§«]€ˆö™ý»<)°taì wwʽ§¸_VÏJ §¦+Òˆþ"›¢pÓ"?ö8U¯ ×Y»* ½´b\þl#r¶’K€„ÜØ=#BÉwâpëááëÓ²„½J°Í®: ¬Ð‰0ÄØ¨Y©õ¡ý%8[jÄV”³È_!c¨ù¸„"[èJÁ1?R6Alqù¡xæQäÒŠDP‰wY)Á¸±H&‹ÞÏ97ø¢Jfäîj<Þ{ûö­Ûô{nà1>‹Šƒ«5?H¯÷˜ûþ šxJš¶IŒRÇ“Â)Çd‚%‹ÒkØf™ŠBnAØkÞ†kNkS*ŒÁº±®XÊ´$LüR² 2d•wÇçdŒ«¹nz ˃Ï2pPd‰±j'84r<ªNò.KO㻑n¡ËÎÅ®oŠ·³Æ‹"³±­Æ#½¯5Y.âЩYu7AÅ4F/œØü9B.Ï3è‰oFB0e…þÜ›¸ÇÔ~;rçütgíÞk‡“õK‘F ‘›‚1z¹­Uo»Ìæ#jÄtZdë5cºS÷"2±ÂZäŸéoÅÕŽ¦2W ô·Ù`Ž!XƒëЯ«Eô$·ës‰÷§ÍË]kõÇŠ&nƸ‰Rgk«ð(Ë;˜œqæ’º dýpé€j]+ÏÌRÒ .ø¿Jåw‡%ßMÚѯ=ùÊg¯þAýiê.rr{ÚA»œ¬ y±Ä4ª‘ü*‹w•Ô3ï¯1¯ß0‹èÕÄ?z.‰ôÙò• (ÑÊ…¨p¡»wn[SHÍh1Y§]øžaý¡übAç5Æؼ£1bçá[·K0M®‚ƒù„’ôå ðNôùž°Š÷Ñ(÷Ý3 ¢OÉÙØ$“ zBIC‚Ö2ó¥þì¥ÖÕWÎ*޾*1µ:óÄŸå«ì\ŽnØË˜$“¶ïL´Êó½œb :¥E˜í2þr.*W¬ÐWç€÷Êe *ÖbWVk0ôÕðXDã¦$ÕR\î`J†¯jò‘¥MÇTóñ¥\g:­Áœ¤, ç d¶Ój)z<ˆ X'îRøqÝú'\ç6Ëyhh W—:Es¨‚~ƒÒ¸ Ýî×g¿XdÕðïêu†ÉÖç–EÓnÁþ¿€g_7aqÕ_¡T<2%~.Á/FζOˆ>ñj:£0lº-}5FoÖCSãfʵz9ñêX`†Bi+VÝ]…¥ü^G@å9ä$KøöñHm°fGO-ƤaûVúˆAj2©™2S”›?¸<Û"2(µsrRꙿ=µ0ónó¢¾1d˜–š ÓXùÙôLK|-ÁqDÍîb¢fg(+·½X|‘ª¡¤å]{ÓGú‡gsFeÏ “¨§yÚÀP)Àu°‚j­w" Xpv«þLÒ5sÓ:Ìþ¯#L&u–ŸãÛã“ë‡ñí= ŒŒ[ü4_kØ-ùWþÿÃêêü¹âÍö7’Çjƒ¤B\Šæ(ê¶Üû*žî2{s‰$Å4IœÃ“Ž$œ}÷l:§Óýã»6Íî †FÌ2$ä¸Ãòæ'\€dÔLôq8Ôk›¤ÇjF<­× ÇcEßžìxø»®=Jº=V~b¢†DÍ¢ðŠŽ¢ê¥|-_»!ÌÕKJ»ô-‹q‰‰³D©:§ˆ¯'KÉÏlÞ_'Ÿát{’ÁXï»Åµ©xãTÓ–ÑŒ<¢ƒj¸ÊM¯?ò‘ýñ³±QÝkæ]úsý·¶×Mì%ˆ’ÚX—Z9ef¨ÆŽˆ‡Ðì¢A+jv/™–#¤ÖÃã vöu˜¢Ã*’1uQ«é³†áúrï©~õ»²sù¥P7XÌA¶lN{Õ{^²:j0duü1\_Ú"›GæU¼T?®;Ò$‘¾¶¼ ¼wHŠØÆUsÞ?}*'͵Ià½V‰6w#/ äÈh)ßàÁçoú ®KÉZð­ IäI'))Øá°¢óãŸ(¶$¦I#Ìhë©9‚ýiVþÅzòxF)(LFÉïÀ#<œu¦9Ô-ûˆ‰4$®Ä^S¶g÷@ì0½<§&×4º»ƒã? u"§ÝÜvböTïYäpá¯Ò˜ Î[cƒi¬±³´Ç_&×èb˜o>Cq9«’92š4² Þd@¶ãC÷Ú9M£ö¦)èw^4áVÏv¤þ±Ú ÅÏ¿Y@vÅ—]puèûÞ-û] 0Ùs-‚=ºšÇué_ AóôÈNZä[18¹äÁjÄ0`8¿¸ÂƈþO‚zØÃø:»Ôä¯oÓTì|ßÿ«3èº9ÉÃÄí§±àn­¹Êc>ðÈžbK–r:®âÀáU )åE¨Xlþ9é™u$4§Ý‰ÕÞ×ÿIrãPx¼õ%Q˜\,È™æUô Ð1<°`û¦»3m5 í v²¶'ÜËd#‚ê„>ó8± Ƽ•Ôzú|Òs$û¶d[i‘4εâ>‡AÙÜ;ã`p¡uþ|ö ’íPÊ®?â}ã@K>@-«ïy8`•¼äé«tðçÎï;l‹ü[v =5„c¢´Ë!\{VeÑYT³¢ØRöÒܽ°z­iôiE¼ØëÁ˜¾gŒ^?וöÑËIV×61CøÒx¹ós1?Œíû0Nb‘ÆAóy ‰fêêUmªìùφv}`$l¤½­'íš›ÛÈ ÿg+o¾pÚ&a¯ŸRÿæ“÷Aýæ8ÃïÆ;§ &³¹ªX©l¡× ù~\I®­Ê%}2ãÔºY$#²nT†ña-k€P+fˆS±7Òv´Þð¬°Vf\»ÆÚ˜=±ÿ4„Š» ݈o}™œÃ¤«#ô fbýDüAR°D€¼’‘Œ┨ãuînͰL¯3S™˜ËJË.&¡ZÏ©ÿ§ÄÉ* Aý#œé<ò‘:-gr‡š:ñq+s@dÇÑ}¹8̬°—Öæ°ïº#Q_¯g:í´­û8™Ø›ý0çx‚êÊ3TRp.œ< —O´:ÏÈ•TùÁé·r‹ì.7ÙÿÄ3±RÑxN¡“#ç-¶eA®+¨Ç¢¬{fgÐ/8;wüNűeÀé‘DØ·Ë}„v¤Îæ¿„llùd™Ù[upÐpGÖÐrÝ3눘JQàs½~-¼gŽ%ò R´é}–_|&æî™ ·ò«gŒ$¦ÚÇdj V#ï–…4¨ÐΜWö=jSQÊÈvíG|•ó_ú¶C+®B~Ï>ŸY‘Ø[e)O÷ŽYÙzg·JÄø¸Ú‚8l¸‰ØFõu®ÐA˜ä?:'`Ô°kÍH§kêǬAãc[hÇ*È×QT6ØqÝpD»3y`­)wo*H +-î_ïL’O:18ybù¸Ð–«oôê\l2¶É)_ÔÄ4 ©•ìÓƒÑ;á­á)„CM~˜7ÄH•90y’Œëm®U*p.àôCΈXîQü1{k7ð²÷ ®1#W¼+ ¶²"1áéÇp@ö jîÓÅi {[ØâKHÞã2 ­d$¸¡"Ùe3ÄÞœ"Þ½”["JØzµÇê ¶Ñ œ@ØÓÓ$½lBÃò¾¢aaù¯~‰o-×µµ_ÿÁYáö\Bñ_–±ê0ˆ³ƒYÚtÀÜÏv:ÏÎnÂwv‚Ûòsxç¶×¨;=ŽÚb˜Ã  Á@¨I7n@5Ehù¢€öÓ"ÑåÄâfÏ•ïN›y‹"µø!-]ÝZ.Â{Ɔân}ø ë¿4‚ë Çd3¯V—b$“¼O>ïäuD (øk¯3\Â[ƒ÷vL8ЉÐ¥ÈÊgMgœÁ‡®ÄíB^úèBaJ(qUìòœ§Ô_ù”wÞšY¶p,©}¼t#)ç;áêŒF`½N ¶~þn³ÔLŒ_Y Cß•&>aš)•.FÒ¥ Œ4);PŒƒÞµƒlK<µ¯Y‚»°"ÿsyãé{Çv–Ë2Gl]Vþ ׈íëIÿ¨ÿr¢DÙ(ÓŸ=«êõù¸=fŒÉo-íºX˜Vk"ö!-9ÇÄzR=°Cgl\1¿+|tÉò£;wî\‘ˆTTЇy®L‡Ê3e]I,®ùŒ»Ì–¤‹²{Nd§B ß–f·žÚRÒe«˜ZÄ4 Ëv)8t¿46¤ƒüÈñI^ØîQ±`k×±)»ÁܘŸ€õ¡Úá¹hχӲÄe©Kd¬ÞxÒ7^ÄKæÈ΋í‚ÇZÉWjuÂ1_ˆ5,±üÉЧ–òYSChÿe‚¯IÀËèS‚¯2󵼇Ù5î}åâÈ 9/¢vmìM6S-w/ ¿…–O%p‚j‚}ȼ<á=Ι8õ ñ.|h‡¦|˜Š)*óÊêKRÐ6$4–ü x}·wjl[=Ê%Æ(%Ä©Uè ”®ˆ§é`¶Ý ;Ýã-O›kegn©BÌõy]Ƭ-çÃìà®&ª ½O®q$´aªÙÜœHzŽéjÿîÙX.–ó‹¡Œñý¿7/|¼ú!ªÀ.²þ“2§ŠÖw±+ׇC\’?~© n¥ï+ðÈûd9±zSž8¹¯gR'{o…[ »8Ä<ÒË0G¼Ç~³Æ74c¥ (¨OkÎÂàß!n0¡Ý÷”qÎac=‘]ÀcMÐÁ3]N‹2GûœŽ¹7³´=™|NíÑ©ï !uVTÛtÆì`®|¥î<¹Ý¹ó¶‡å£P ÚÎľ̜Ò‰zR[$%ØàŒšÌ¾oñöD>¹+Û¡wýˆt*Ê'³Ã¶ƒ"îÊù# VœAÎöÉÑÞ·¹c` 1‹eìÖk~öüϘR-Ù2ÎXËœBºîÁ? ¿dš¼ùS{§\¨}?–œÛ©2å+•í:HŒþ¶3†[LO”Ý × Ö…s·m ืCb¼õI°ð” ^= d¶†ëH›îIEd_Œ¶¤¬à‡é žþO F¡—ç¥' tPÚ^蹚XQ”ɾAÞî·tñüގǼZ£F©5Õæt]ÎD>Qø43Úb[±^¸jY`{} ™Û«7öò8Bë*:D£=>CÉR:NG6ו.à@C›#©ÄÞo©B^ìÜ·Ö>?7ÎsêZ‚`v§ñf¬«Ô2ÇĆ6íc™™ ƒŸ>örñ2“É‘$´v$±D0he_0KðÔt@"?~/^û"p¹4Î_üœ\°SÂ3˜‚Ó8";Ê]P·ýgLdfcGú¥¹Td×h7‚hQ²ù…`aª.CÈVÎ…e¸>Þù€Gú"8§], Ü Q&§<×<Ò80+ǃ)AŒÆd8ݼbG°t|H7Pùø™zá›]]öÒø”—¼úü0Ô©v3à>Tª/MÍÿô à÷?aš£TëGÎ횪!ç–ƒ«Ð:0Å=ye㻆Çdâ6Ð †m1Á,F[ÈÂ5§;]ä˹ƕïjéhÉ_½<­Ükl=ÛÛßêC|/ÊÛFIóAÄqK^ñ178J€~žõ€ÏA[WKùEv\ZØÍøÐ%"')/ÍÔ’ß5ß‚ªAXǼ¹ë3°oÅ ¨–¦®ÍKhR&Ÿ U Sû¹hq9¼—Tw®û5ŠÕ| ?*5hYó‰j)ÆÔfjX¬NV`ü˜\@Á7êâ8 ‹Þ0aš¦–waþì¾Dˆá’NvÜ9#Ä‚‹?€vAa´ÆÜ¸n¨èõÖHËzŠ#Hg…1/3wjÕFnó~ôFç.§l=ìéjõ¸¢+Ûñ¶~[oqþ6Æ¢êo4Á\ Ûg'ÿìË`Ê@lè9Ÿ‚汜iLÚñ~mfüýR´ô5wß̺q"u‚ÑZFì*SPÂ>Á\éÝÎÅl‘E$·öAÚ£´³-ù€5G_0¨IÌ=¸ÑM ê …Î-„û–cN¸Ð­zˆª$:V÷ˆOÄ„X]KY–tЭ¦öì°ë²m¡ÂÏW»µÅ¶f“¿ëgh¬™zNÌTí¨th¢+ˆ¿ûϯբ_p!z«l(lyì÷E,úàAóìñv6"khžÖ=¹ý?BÞ1|Ö}4K¢í¶” ³iõ«28;*®,–‹7[æÖ3¸&BZ`+úÓÇcc–èFГ®ŽŽwíÔRȃÞ(“]g¤#òÁŒ®.,ÝÕ’U¡àDKFµ ê¢.v™ÖÁáè§ ˆ€’ëC0lÄ;’`õ4„6rˆù’wÉúÖ¯RÑ÷¯yØ)Áì²™™ä›m²*Ý=Éè^™Ñ²ç(/ïm<¶W Ìõžêù9‹ä×Þѽ(st6y,¹p¡WÝïÀ×j\wÑž²è)³¨MMÀ2g…t\ïÔh )à¿ûÖ~Þˆ$ÆÖ9ÁI^Oâ€fC·^•éU÷9ý‚Ø=S] ýNHŸ9dã 8~V§‘Ö³CûP‘zÕ ¦^úgK7%¢5v4–Í•#ì‹pƒ!÷4Š_—ná“¥x†A€zó7’&³ÈÌ©k¼v‡L 9Aw½òwþ /ž< R×n"kˆrHRí玭FïxìÖ¹-2í?Ji*<‹ÚâzxgtmFs“¯A?!1jí#>技ï¬ëÖæÈ3ïY*Ü{U5¹,òG¡>Ó iÀÕœàç(À/f¾#ÌñŠ’™>­çkvSSPÀç˜ ¤zm[¬n%cw줗„¹°¥-­l¯Ew±ÆBºþ û¼G¼¸\‘âê»®#oCí?×)Ò§A—ð‘Ä…ÝFm„P7f¤V~-ò ÇóƒaÉ´ØFuÉPªWÇ6ËCZD½>Ãh'›ß׎‡ó §áœ<#[:â¥á/7¾,3 €{v‰ÆŸ‹‚¡úª£ÈÝ»¿ºtÒâ¢ÉLqñCdéöüß9\@ÅM\G|jÓþ,f0^]€ê(+¸qÝÅ÷æ‚!¢<êc±L.*o'ëðÕØØgÛ‚Ä 8*\Æ•O³¡X•™S»Œd)_7Ó!ýi“ÑXó’-^ð ëÚ…÷/z>pJegRQ¿¾9M’ßñðÌq0H™eT¥ ô&ÉÍV±o šÙ _kØ ÄµòxHK½]¼O©1aòÜVȽfœ×øíä……XRs]ã ÷ `Ùãtg{Ô3¨ëc>6|ȵ¥%²è²Å¯s]=æÛ#–{>i]J²5¶˜Ž]:ï´1ãg0\Aÿt±É™ë(gpH 0ëðÚI«Ù@—;".²0'ÐdJƒ‡j‘e±ðÊŠ¸>'øŸ+Ie<‡ãvÿX©€Ö:´p½Í‹ŠÓJøÀÐ ¢¡?S0) ÉIñ­Ù=;ó¸Å¥qwÊ­-VZ…k½Ú1H¿ºÂ^|‚PqgùÈEot´83ÖÜš¶°Ç`Ì#ÏÛ  |¯å˜qhÃP£Å3é bh J•ÙøBÞVRMG—Àoι<òZ•Ä.¢}ÅêœyyL´v9þ#®ÉÈ_L ` ‰GÜÇ|`‘ï®»YŽœÜèî# †un37Ê®¡2P®lÿNqصYYÉÄ€g|½½+º¸Ct·‘_· WW^oCì$èJÕ¸ áéx“¤øÉŽßa)±g„ßmH"3 8ýï=gœ¾¬KîUXýÎÒ$>;ß×B•ÛQ$" VI4ö Äœ Lôò†‹MD*Ьšýíu_?Äô¡éÍAëÚªVJ n]j&Ó„XöÏ,ñJ†‡7êßв޼3ÒinE‚‰7eg¹D4£ËJ œ°¸Ÿ›ù²ØH…­œÒÑmjvDuÊõxSÁ½!‘lܶFg€òu„ݦIIãš ¾‰òF¶Ô¹üc(Ó^nþ†¸ê¾MqW©Vˆ-ë7u|#êî….îø°@oVgî®CÈ™`aæá1Ê:¥¦g¿á+¢QôëV{¤»"µµjÍ\"Ãe$F…·EÝ¡Ö Ç5Á'EqŒQ­‰QÂjãD’é8w¬±éiñ°—"ÚëcéÆ4ïX˜·«é°óÔ»÷ÒڦͶäÔµA~Óï‹pÖÜÝ\BWÓÖ“ݺu(¼$D¨Sp´l" õ˜Ï¯Üc‚azx5hþŒÇÙ£|þ˃çQxwzažo¾:`Œs‰øz±äm¼T·Ì¡óµÊ0^—--€Eîq•kÍf‘‹¯V¿Ü, Ö‘Fâñ°å‘ž„žçÚ-µ>ì›rõÃx¾A ¥ÓG€®óüÕY.pcÕ“ L^Ço=2§ I&žIÒ!V(¯øÎ¤>ø+ÅÌïC÷¿Ë‹'å>±=âSÚc uo»V?R:oÙ*&ê¬Ì_j¬:ãvZ}"wq”¶]¼)Ž+òeÇÿ“%¨}ÿ•’â›͵ùî7y^k|®;Ðð § ´+,‹¸ ñ±o-X;¸³ûïq•©œ4ñ:H§-„Æ–CÔa¹ B|%>â)ôRË õ¶7î×›?õÆ@`•–zôêE:¥“R×ÝN„!¡°º(ÇÖ¦GDËhø³þ¯hå÷¥ŒîŽ1žõiÃ&ï$!X-Î+Ýþ€Rõt=m.QGoŒ’$ûô¨Ò¨ö°îý¨ [ÆcçwÌÂqÄ{ºœjYÕKÚ*éu™?0óû˜~$–1:è¾W°-~·È½ç‚ßOÂr³™¦ºâÕù*.=ôóÿ8išîkàt“Òi9ª‘ æ4×Ei‡]ÚÙ«˜Nq‘ŒeÌ×kÆýÏm×¶jb1Ä:.X­‘¬,[x¡So9ú#’ׇÎÞü„èx¯PÃ-ÃqÀÕáaùZ3¯¨KyûbÍ¥ˆÿŒ³½ç]‘%9Áг1g²>Å $±¨ Ç‡{Ï”­k~LÓ:rmRÛ מñPh;‰d¯/'làšYIL~±¼Ú:ÉçW9i@Ý€Áî e<»¿bôt>6­ •M…fÃÑxÉ(dÆ«Ž†HW+ž‰šHå¬sš×ë]Êx9Ç]ŽGÆ-d¡°.ñ˜)Stk× “øD<Ž7­öf¹¢ˆ0Ö´Ã=6Y±Wàyø¸pÿöjTn@ÿBz£ÕÝ‹’uuБZhFÖFÄð¯Jg´ÏÜÛ€Ó× ÁmÓ‹®"Ž g4‹{T%£ëX„€ˆî¨J*Òu7ÞÀz€§”ˆ„yúL™„}ØäübÇ”\qºÜg>Lþ¡Îúv^Æt\,é\þK%îïyÚˆ ?ÎÁÎQ¿ËMkŸ9áŸåt×ßÏ)¼(6Oüª{«qÄÞS¦²24zVnka;ü±ó¥Uh¯%ö]MåútEŽ{ )»ÞÊ=“|o/ZhøÉ|Ç?²šÊ2ºøky>9¡N)!F&-n·^¿ùù +@zˆZàQ.§º9ý§×"Í8èmy 8¶Ý—ÑÍÈ[ºÔa>±^½q‡x‚À°é¹[¬"Ñœ]¶RlÚs è;fsÊÅíK°y×7A€_U0Õ3jH•Ð2tpÅKôcEcùLŸÿ„ÅS wª[D¶ Ò0cÿcÏ x× d껤F=zÏ{Ja0ÓøÑSá‚ò0œ©lx”ù†"·¬§0ø‹G`GÁo>¬¨†öèÁÍá1¯Ó~‡9ÆÉôÚ±ãIÓ=ösGŒ­ÒÈH»uˆõJ˜æ#‹~ÉL6RÌ‘Lˆ>Œžãß’íÝ¥ðpK¦PI NÌî M’¹ª—âRçVž ýVË–  ç#‰ ×[†RWÑÞ<@¡3‹ê»Ë¦}qt­†kñS:F›ÑæŸ âv+þg]Ïe'D±‹+æE[æÿ1jð^©C¥´±NKnêƒ"ȲsÖË”qPKïæ“/)¹)Ýý,dÉENIÈÎõÞQúx×áY©¸ÖÜópÈ‹ZÙk ±¯MgšÍ ôöï—¦¯cÆÅƒ³”C –x4ŽŸò¹¿i´\hëùB˜ÛÉy#úC„ƒáx#·|I˜AÊ’8ÊóÄŒ\Z±€Û;n¬b´{Æ£¶«ËO­ùBJT‰gxtj®±*ºJͧoÝ3³‡ä˜wèÛŸbñ‹kÑSú¤PB@€}’"XO·ôo2T<Ë—€m¹»š­£ ”;H½Gú²1Ò{˜e·YX†·^m¦ q;<Î `mbx,W(t\YKÔócr4Y> Ing§Ô‡C"WÇÛ ÷ɦ nåÂo«².K)RØðSÚ5!ï]¢W¶@8ä…Â׆)ã˜bGݰ8Å |Ùƒæ>zð»Õ]jy9Є:y'†h€,cðÃŒªÆDŽª·¸«Wᚆοµ:œü:dšXcCß¡¼Ðà¹þ¦¾Öëãä‚K1ðEž\„pxÿMÕ’µ&„7b­m,K÷± -®ó9g÷Ûä Æ0 ¿xUGGàL㼬Ý5 ÄN‰¤àS׿2Ç›0¸°»Ý*8ó׈òQM}e×eG¨ ¼fhpÚ¶ÑsE îBwùÐØvwÒ»O™}7(_BÐ6Ä·ØÏ€Þ´ÔŠÂå“)À.\18ò–Ô z”Ð ¸”ã–ê; @Œ…Ûç%j•ª‡‰ƒXTuÈr„•¤‘»K`ŠyY8ñB »ß4™a¼–­Œ†ß%¨Ô˜Œõ„(u…™š˜J! dÀ5Áÿ^ÍÜ_ú€|`Íî²UÚV–”;Ÿ§¤®RùÙ×øë¦„Ò ©6ü©\"Wã"yM2î±NˆÕiBJF%2A–]œ§¡Á ¬Á°"G!±ïºÅ2HYº{ÒQ® €u9ù€XÏôci|Ù‘Qîp)ÚâcÕº‰Gü™[¤0ìm@ª,ºè\äZªx\¥õá¡ ¿°1&Úó°ÆZô3\”6Xb`ÀJ¼ÿ ™ŽÃEºÏn:M|nÇ”@+ã,߸?{e7z†hQ]ù…U ‰ìó¿ÛÉ3ë– ±ïΰèÚ#E\îÿ¾±ÃŠ’¸̶„€ø¸0Aÿj„Ì¢:æÇkáÄ­¿ÂD².ç¥¤Æ õx«Iñð‘d'™ëß &÷Ú¾—¡–‚†þo«V/´n#º¨s÷â>–(àÓ;(Ü+냀ŠtÀ•§Hÿß5µcG¡ Th^̯ÃSÒ1ñœ¥Ø·eîSçìv? òêãk7¨C§Æ4@ Ý®my²Ø—c…‚Eöh…–a\"áûŸ…:MIn®ÃÇâ,м£.g£x̤:‡æŽËqå©í­–¡Í§ ÆQ‘&„øíz%‰`ô™mkL¾&îÁrðÚ°š W7ÚtÑ|:×wüÔ6ˆè¢£”È>{4+¦€ðEœ¬­{ì¯jé1¸Ì½ÝtÔÜniî½¢RK-{yŠA!<ÛðaŸÚ†¶ZQC~f»}ð îÌô$3 b¡¥|)çXcrÖ`§Uݾ¤àÓ^£¾j//® ™1nfpGì®Þz­ìq¸~wØ=nî(CjÜ€Úú%îôu­”³çñÝ*o§Wm"§ ΊC‚WåÅÜÑvYÈ·màA`M.ˆ#Ö~~RᵦˆA å Ÿð½EêÁàvmÀpäÍɺרC¤á}ÛäY†{B•ãIËÓÖ~HpëÆZ\1*pw:ÀËãE× ê—qÚš•È>¹TáÈykŸÃ—3é‚=4îX_hãI¼à  !Ø`ÝÚA<ñ“Øc)å^5.ÄpHç¢ZÒµçbˆíâ®°c $\²¾ìˆ¨O<ÏÍ`dl¨îv˜ÀtÖã×?,wF¨™vùÛª5²òçÿ¤šãÃJ;ƒHnýƒ¤qÍ)û®RÐs-Û`Ý“¸p)èúÙŠ•NÞ8{ï£;{Ÿ„[ó¾3˜‡[:ä5”‹g49ÐEhjN!c|ôúMu=ÊrE­æ_Î%}m´Ô ^:÷~€å“â;Ê=¢÷Iàu,›]_Žqm•ï #Ìß2®Â¨xøùÜ.…eÇÓÜ0‘ZüòúØó»2Šß¾ù»sqQwV´R¡Ör²nƒÃ6ßx¥è ?ècüú^&‹FÊÈ(3UQS©ª»{sWB¸sSûƒç‘Œ|vl'q.òÈåÕÖõOÞ¿ ø'å:£aoi)‡ôïBÌ-B‡V&2†‰Ê‡¥6Uþ´2r=\æ¾JÂ}*kaü#MS0ŸC~ô*hñ…b+¢b•IØÙ•–ª^¶º{W-ÈyíѰ²Ÿôp‹Šcœ;©e‹¸d¿0¿)S|:„ÅÊkaH[V/ž¹À蟟sÉ뎫Üv¨b]Z ù1£ËdÚœ vYiW-ø/hU܈ÆÔÕüÊ·yëkâðxìÛ1µkY:ËN\¯Àš$Âöwûù”ŠðìFìU´|É2:x3ñË|;”iìN'š6¿·²¨Í™È'‚´¿Gmb:Åà#w„-txö™–ä´Ed"ܼ§ýrJë@,çèHßÇGÝ5íï´¶`h„Kðk#ØÍcPÍܼ ’ó[=w„ZIyó¦)bD‘îdTYyÍsl—Šñû†^eP1htÀR ¤­Ÿ¸À?–L(݃/éwäׯI…µ¤ §nÍ!eUˆì«¢ƒcÂÓSIosÁ…]þ«¿Xó"@ %y¯ k‚{ô6L¨[Á%2I FÖ¾›*н‰æ+Šé´¶úbq¨¾°ý-Y`ѽ £_šx±Nœæ+>™·8 G1\¹„Œ¿âmEÕð!¿½3ÑÒž|û,•¹mwà?…èôëЪlVd5…ä$·2 FÉuÚ|”©wºcó£›Kv«+* !‡´„€-¸üó’ò(Ÿ‚£,þfÁ9óY?jú ®î!±ÌÇ.Ÿbºå’o$UJ’<¬ZÝ‘|¹/ßU¯KýZBPª³^]-Ú—cÖsV!#f(ØlZÀ’Ö¸Y9ĘÍcZŒ[´®‰êµŸ) */¢œ‚"u_Z‹TÝÕ¥¼ít'Žœ—h ôõ¼ºÛ6®&-ü_u{°»3E·oÑ­|û›*ßqJ ]^œÁksyÏŒ‚áÙìaÕnnï2åÝçmdßâo›nì¼S÷Œ#‡è´¯ßɼç‹ß#ô6ªëÿäJ2:¦Ívû{º¾…0S£DÀՌڲ]§Ô8“<€p¦ñ&¬IÏ$7> ˆó‡?³ò>Ÿ¡§X{Ž”È„^¿Yè(íšÍ´[·}ë{Î $ŽïDvHŠºz}.} ˆÙѦ~Ø¿2^÷È(T ½d Òf} ÍG¾qºÄùZ¦BfÛDpî=.Y[r ”ònùtT¦0•ï_!ÜÎcEb!"U‘ÿD4l|8ÒŒ·,Ô[\Á«­J˜îŠL£²ÉF;¾o×OygOï•*côæñ±QfÄ'(ä3ÿ(ØÚ¯¬þïn-`‰½ùE²*R%‘äl3ŒUÛ° ’ ‚Ù¢†˜q¥¤KØ^¿ x˺µ;”&ÞD=&޹¾ê©’£®ó$RGó|g¯ùA ÁܵµåFæÁÄ-æµ.lAo¿¬»šI¦ Ž?ÓŒ°ÓMŽØc_'Í&ºgV92 )™Î Z€B§ØwψN âäɔ쌦ì)Ÿ¥„K@/áÈñG ;ÿ{¸'늛èÀ÷åe·@è8ÄÌ9eÀ£–ÏŸ—sÅœ«þw˜S»py{BLÚ<Íæ¬› "SU6s¶l1¿Ï¯RqS>"5>'ÞµôÆ¥×å À˜Ò­û–|±ÓvWŸ(È3ÿ÷ð§Áß~ŸSÒŒ‚7ô‡85(BK˜ºîÅ×>$¹H†ßÉ)˜´¥}‰¢yF€hv´Ö»ø{aHcù^¶ÄYW“ᨲ<ÜïoH‰—îd¨S_5û¼•}¹y5|ët¦$êuáDÂÖs<£i_¶G“¨ËàÕ¿B7ú`X¥µb~œAYÐ Wý–Š×!¸ˆÊa¸¬¶×±I¶ÜÔY,Í*Qî€ó˜º"Èwh0å1/Wý«NÅ`ùÎý/üÑnþf4ªåò2s×±ÞÇw —BƒAEa 0sfOze…k!{­2•?±5‰ªƒ^3½HÈå¤zü»{ž ‰Å>HjÛþ".>Ìyá.«£W¯”ñô¶:S¢ íRÂ6”/6z¤šØj`6¹½ÍGÙn®±bÄò¹ËÍ\Ã+i()åó×çÉ"ÖŽ¸4á´‚ø³wc]PÕÙ£ÁJ$ .áKÓÈ›2—¸/±-…ÊŠµù¥l¤ðtT=2s¦‘•Ä% £ßÓ€ƒoAv§–¾OJšŽmÕ5VóoºÈàßø}òGeLfB1Nùbãx³fÿó>­J+üA±e hzÕŒ%&éMÕ¥K¯ã‘`]I®RÿW—E¹- @"YJÁË8DX¿"_ í7 OÚàiwªùJ•m/eO÷P©3»aÿCßÇi%¼B…ªþAr g BVð$ºdñ|p¼%^3Ê 0Ñé²xª›OYÐÉ7ßeñ"ðêŽ_i½Æ×§‚fÈrðü ë±|Æ>Õ,ä-|½RñÑÒmÍ,ߎ-h‘×.ïTÝYFrÑõ'öÝês‡ÎøM0d…í”Vbn޽Ì7ÊÀ”ÐC]ÂÈ€·«: »ïÙÀp_¡->Í^qcãìPOW.‰åÅí§§†5RŠqr„7ûÙö'Ó©ÚhÅ¢÷íAr¦­‰­ƒÄà÷÷¬NvŸíÆ)ò-ØfK5^ór¨jàåíw´~S¦ùR®ªŽ jxKT,H â•$IŸ÷ua½ “}ÂÀ/“Öˆ— ä÷Ï×Ù-¹¿¥ÒBг¥5ÊÕã¹bõ@iÜš;±…ˆ‡gÄÇ¡Ÿ A×ã&Ò¬{m½˜™ýÌ=Œu…_U°üÅbæÜ£P˜**ïæ“²<ߊíhXð·ôî·ëvšRdÍ€ò,`5›5Pr_¯Iχ¥‰MbsQPozáUo»ãPu.—òN·´™Âˆ¥þ¹\“™T§'Ø#Fm²œ€Ÿ |âm„KÊðqÅ(Á}ÈŸ½®ÊùŸ©ët¹-ÉÒ©;W¨#î ^ü ñÁˆWå 2£×í€ ¾dÃÇöñ÷½ É|”˜­‡Å­õû”ÀÚGãzí¥›¿øŠ|é«M±¹ççœÿâk.ýW7.àùTnL„0gsM^ά­ }ô Ù¤}ÖèTн#KsHZÆIêdHëiûê¯ùt”j›àR>^Šs0íè:5Ãò·ZŸôékn¤äå¦j.“’Pa´šx/>Oß¹ãÀÚ µ{éˆc¸¢¶ZïÖH¨¿±J oÙ44iÛ“§^ðO—„·qïÅ2’c¨5ÌñËÖÓ—M­Úúìr¸ÿÿS½©öI—bÔOî¤b2ÙÀ†™ã(™a½'£0Qëœw6>:Ô+âëóì<š Éçmý`ŸÖ›¸­±‡è}ôbzEK{ÕBP±‡?:òáCÝ:)ŸÐ±çá«4ŸÑ8mÈ„¡¬ŽíÕ‚½-²7™É”¿S¨áÿ˜äàZèè3ñÃÅp‹u&2ö0!øÞÚY]ëSƒ/ús¥#®œ H *|š½žuÊI#¶,ÇÞµåÕ#- ¬ÛøNÎ'ÂhÝw^ä·ø¥’˜êõ'Æu:×Ck~Æç÷èpó€d}&™¯é:Ÿp@àÅ,ËlŠC¦jß H‰÷u]îyƘ.#±F,ZnÌP›Àæáþ9x@ÉóA»„“ ‰·–«FÀfć ~̯ ËMeʺ1£ÝO·ôŠŽRÝüa5÷X6óô,8jñ7¥ø àØ’Ž Ï>[µE ¯‹×É‘—J»ÓÜ]#±âmªF„Ü@SOáÑGò^·UsE°Ù0§ÄèPép·mSÚßuŠ} ü™,\ÎÛÝÄy‚ev↠Š,dl~ÕcH6pªl6냩AE~`S0ÏÎã%ï½Á•3ør“6«Õœ¹ÀÞq÷Ro’Œ¯°‰t ÁLøtUEîUBûØÚøN…!…¾·eN”€BMM„o \ ;BNãDq…ZÖ»ÝÒî½Éz‹ÌòDÕEL_ǘy“#Šž0Z _KL¾÷:i|èé6 æ9ô.îC k [aÊ-jH໨'Ö>X¢t ÷·0yÓ2¿˜>¦Ñ[1ìbãÂ+E"è'þq<˜60e‚Ÿ£XR»a¤ÜðÞÍÔ{u—%oCq©ývëW6PÓÇð¸›Ph}ËR”¡9¤©¢ülGPE((B*þõÕG„¿¾lIÁ±Å}›Ñ…Ÿ³;,kÞ_ÙÍao¦®yÛÿ¦;i‚É|=¥ H¸\f¥Á˰Fç\ëØ³þlËdS®&æ[Âjãh3'^¤Á-çŠèH¢ÆÑÄñC}ñ€œ]“ôY¡-}<Ö s ’;ϰð]Íõ81xÌ–v9ʹ¼ÅeŸ£j°u¦•¸YMS?á̘fÞ’ø×‹lÛºÇ(=ÿì¼Ézå*Ì\o•%óð”ØÍFI6­UÛ„0Px´˜ÊF~iGO o0p&òÝ1h>Ô îU±`ñ»eýâ48u7—Œ½Héù[0þñ9S¨>8Ç–zÄŒ±émõ~ò’$”¹èɇNLÅv±—ikÓ«)I5•äU˜ˆ'/¥÷Ùã †L3àš‘íN¹mT"KyµösX´&¬xÙ#QˆõŠ¸Ðæo4Xv²yq)(0…6:uêÃÒAÅS^e3ó /ßRÓ­“z>+ÛÛHB3¬EZ>ˆò$gj‚ÍF;Ǯţyãëæ ÷õÐ…æ^ü >}šÝñÆå. BûÆ‘ޱ†Æ¤DÖN¤Ü¡}t¢W~¼D@¿×0Îê”Ùå¶^”ÌPåçÏoÑ®©QÝ;ÿ«Ì˜ÞðLö…ò/A™%ÊwuZù:ôTÈUn¨|œv¯ºV\³=¦|²®`óDã áR­üæ°øUU§D¨6¾®ÛšWg’²‰ ø êE)1Ü» G‰éste!øñ…ó1¤ñÉ'‡ühQÛ‘?Mh±íxy¿|O¦¬´XPýÍp|°<ÿ+ýàÆK`# f.7åî©ûf7G2;u.”38½Ãþ'ýŽ…Zó¡%½ú¬A ºIÏ„GœJ’¾îºèfd¨27oV›+^a‚dÉ`•äºÙJÉ" ÓNoRÓÎb˜z̼ٗ~ŽÅ·þ u¡@œÜŠVR1™tA#$pêƒZ^—a: èhå>\ë¦ez£K”¸ÙFZk'Ì¡l gveŸEñ5ÄÒ–ÊmÓ´ÍióÊ}Ÿ¨$zÁJ—9 ÓçvBÇWÐ×è‹EPžˆÌ|0ߟ¸|LJªƒ»Ç}ïMcGî€úfÙÚ°—乨¡~~q経·øøùQ60~{ž!²Üÿµy6ê£-~ü~6„ñÖÓéX,4¾ŒpÙù0E]°èMGíÔùðˆ/Ü1¥¬~=î8–í.N¤Äò"ؾIÖ^;\‰Ž2S€VæSS]›±u‘P_ùàc|ñA%®è`\­¿z“e­¯¬ôG¢)àŽÙBY[­©* ÷“œÃqÇ›q§â"ätk ‘ƒ¡JþàÔ½U•ÃGH*;#;sP¸æb ·Nñu–ÝJ™ù¶t\NòåuPôGÞÛcïüŠ¥ñاí»Ã#HrN½˜i:¼ÔÉAœÚS[ìÔ$ì š= 2àZ—¿¡d*÷;¾DÉ ¯ŒUÿÍù‡3¹‰r»¸Ã‰«K|{E‘Z˜ £–©{Nâ“ÄÁûŒ9‰ ¹–}æ="˜´Ió³:-%Ôf=ÿi,g|#QÁ]¤S^J|w¥ŒìÜ¡!£_Ò&#î@PM¶šjeAñv÷Ê‘Ô 3„=wK<ÿá¦ë}ÙIþË–K÷“:ðH6—Á¦Í[:¬Ø TqˆÇÀF ½pÒÁB!"Í`>:~ Àî×ë|RUӻþ{”éYÑ,ÊÈÈíRZ­ÙC+x†4Qè{³dKÃ…Þ£¢d.·‘ñ9]òñk^))OÙêÀËÕ$¨’Ú=$?iG£ ²å6M¤X˜È:ú.ÓÝïs‘–o¿]{ûWù6þ&+€\¦O®¨•,Ÿ„tn«9àÂXe€qKŠ»±xRêVS5Ǭvè8J’Ξ"»èø{ ÐùçÐdA¥É³Ž7IO ]ûµ”¨@4©áÃô~Ù ²Ô¡qK\EÚAL™#°’ÿS`dò¢Ç™Ü ì>E‰Uþs¯ÒMç'Ru˜ o¬ko-LÕr%77€¹ûÉÒ– Ïž$}ñdÙ¨IÉ¥ V$)û¤:·´—gÀÈŽ®ÒÏ<@ñÌæ9åÂñ IÍ<—˜NÎ~öÒú­f8¬á:îtS{8-{,Ñ ñ/}Ækj¡6ê{¡¡^ƒ0Ëè.¹ý5Évx‹ýÄ\FX86™ƒÖ¾ê/ø}s² °ªmý/Û%°%yºJò§3HÔSéZrØö¤Ê¹9Éló~‡;î‡R íÈú­|WñÀPÛÇ\ê(qS.~•Á­ñ‘2êÙèüŸsõÂ;aÆDÕ’L4DÜ·¢]ä|mtènMV_MB„œ³tˆ˜œóÓbeÕkŒÁRͪŸH7ð§„Q{·¶¯rÔ:Õë—e\Rýò»økˆ÷D¾ìþíRû±vËoˆŽºš{ ΫöÊu’_ÕqÜj¯Æ•OuzóÌjÈŠãáhÊ×1˜zéÖi@zÑFŠ"ųïzºZ›ÿyª!Zeï†m$ aðwédvÒÓNñ]‚÷&h?ìÆsQ›$²aÎC±CŸ_4¡IÇ•K<„¡¢#Ê i(5Qª¢ôn¤s©pqtú(¡  G…ê~‡m’g®ÿ™ÌøúCU3{@ÿc¢QnwᨕƢ³~”-Rª:Y¥0X—iFñKQ@VˆÁ‰G0 ŽÖ˜ÄL& ­FìÂê!/<—Ô´ú«WÿÞ‘-šãœ™U>üai{3.ßSYqpšûH ølbf3lÄ÷W@ëè¯0ëqñK+2Ün©¬ u¯uáï_e¹¾÷iב ›äÈÆô½”^¥AaR×^¸WϾiSö¥u´Öm)#Y_ñ#N¡¨gÉ>J‰8J¨éAJm5kpHãß 49r©Ê﫸‡J¡•Ó‹è^VèPx ÎVû­LÅâçøµ‹‡fÞ|‡Zê€N7ùúóAë&1Žð«TS.¸s>/ð3.Þj‘Súí~¬aŒøâ1hÃ7ÌoûÉ\txuÎÉ'@ä-@ÚWy´=‘ñÅNIPè2’JÃë$g‹©Ñ“ÛäïÔÅ+›¨XqÒTmÊP†°þEåamµr3½]pâ¯7‰ˆM³0z»þtÞ3­[{ù=Ê׊BÑœxZÖ½v45q¸iÆ dË[nV8Í?ëH°=Æ:ÆÍ`Ûs®:Ǫõ§Êð ǰä% C…’-ÃøÕÖÊý¾ø£QCïø, 0ßÑó=Æ Ãf c<¯ÞUBÅø¾Òþã£`¥k1˜‡Ê—Rù&úÀ÷P.ÈÀ4¦úz]þo­»£ïN+³J@¹2Ly˜^/» IÜ­·.¶·Ç³ÏÅÐc%ô5GŒ)E¯¶¢Š'wŸ>Á€ìBÅ|w·]• Ĭú¬{.io;µÑÇ[(vÃ%¬,J.Â>¯ÿKî÷"ʉ›ù+j™~¼¹‰°"v8|ç»ÔÐ4¿__HfÁÀ”«_>Ìöó'ÕÚÉEî3¹«9Šß2Q9¤U~hßfžR\û†ÅDVytbò¤nóÿ#ýŸ- Í] €¦ý0W–w€{Àö" Y¶{÷ÛúFH’Î\ å|XÀñˆP g3ïÔÏ@k< :—ÚÚJ°î—’[vT;­>û.‹uYWØ5æg†°âb&ÀÙÛ,•m­1}>„Wí|4z²'&£6Õ|=|÷ªôkæEáêueðEoÇWŠöªÌ^Çq×F”OެÀ°­ñˆ*is\³nû& Ùuì壑‚Í¿§®D i—bÓ^¸1N—Wõl¾Â7ŸivÞgÆÁ,­Xhâó žª¾¶ê¸³PýíBÓ1Ûª8B“|J•*î7 A+Â)ÁƒUÀÆ€b(Qç•ü,Äaê>ð òô]ó»_€¼‰ñ û߸ÓÐw+Jƒþî$ Q…™š’AÏ­¯?Aüúiª.£Fµ†‡aò±1·ç6Öª½úڌοP9Ïîž²d¦ £“þu§¾mTX£9ߪßäðk^‘ðø£¾2ÖŒéö’x®*®Ä„Š÷“Âáš‚¢Ú€,àk¤·gÚ[ Ëz³‘Lk§_aQEþÌH©4ç›2Þá²­…Xnž—t`Øä 8Aµ¬‚ø¼;ßÅ “5¯ÑzÁ¬°F,ë‹'£zÊ“äÂz±V”6jåÒTóÞ}`¶C+pŸo3?æ8Õ¿z-u&j¤ÑzÇ(ç_°šá°H§ðœÍަY5üè_ùiÕ÷WV¬ªž×ý—j¹˜§QÆ¢Õ½>ÝÑï“H@¿“ñ'ÎÐØbTt8÷¼Iñ`¡„¦¥Íö]c5h§A#dh×M¯ Z%áѶ'o ÀG‚ç )«ªÖšsèºu©cg4þwåe×Y¤åoîùz¥@ÛKn5v•0l/5IF€'Lo.¯±™?³kŠ›|ꟾÁúŸ«•‘œ4:)1 Ú„Hñªî7S¸„ód?º¹šºF«G¿µL5çJ¢åÒÆÅ™A¶É‡j ï .-3¾Aa–Qj3f=‡¼MêolÉ3÷ŒèZÁ]ÉÖã©Å3=q*2þ—éfP­U @z·2 9ò¦†Ó(Ÿõ›~À™‡ ¨¨µß›T ­ÎöÀçòBnëu¹ÜhœWÐ/ôÞr¢$Sµ“±¿HöGnOínpá7[kÏ RÖñÐ…˜ƒy!YþñS–‘VR‹=OúÞ  wA(€¾.Œ2ÉyÜKœÝröÂN»“áF‘¦¾§P´0K–e˜Ðš"Žj.Ô‚ZIÿa×®ÛPõÜ}ð„7H^W9âþÂðµ¡e"~µ±,=ÁïTo¥”Nª-˜~ËH ûÉÓ±]j3NhÓ²lL ¬£ü Wð/s{>Ü¥Õ„gÿØdŽÇÓjà}þ¦X¦‚u,Þùø4¿^^2ƒkMŸ´LR%WņE!ë–Žÿp‰ìµ–lP[Šê¤dió¦LýñŽ‚}’Ç%åæ¹?XP°•È›×¼çᨊigüÂ$‘ç[{.ÌèùΡ®1VoÖ¥Z~^Ί!BS" ÈqHÎÈ7ôR“HZÒO\ƾÏWØïÁàBÕ†edYÝŽPI0ÏÉö9ÉPÿëb°”`zDÍ÷f½ÂQŒXS`ð‡„ö~¡cp%vCB+¤b®&bT¯†L† ”Ú€­ðËý^Iÿ ËO÷ȧà 7ŽåVò£ß²@§5xøÚ$Ù´-’ÛJà[gm7P]FüØ~º;¹þYî7ûÉ¿f°< ÊSùs¶®¸yz…h5ýL;ÌO˜vv¡¬…ÿ'Ä{4õ‰¨*rÒNvÐþp ‰© 8 ôRÙƒÄ9 FËK7è|ÓÚÔß ¢¶×ÇÚÒù| Ñí—ž' l¸$F}4ða‹L¨®_™_Z7ÿòýMýÎÉýžGz»êpÚÞ É0x«B]ˆ~é¤PU`/þò0:1zN/A8€.b€/¦ùÙðix`²%;ØpìG€ @1€yŽ€ NÀTdar-2.7.15/src/check/Old_format/archive_07.1.dar0000644000175000017500000000244314636066467016032 00000000000000{ø‡TN07yN/ABZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SY8Wå@À 0Ì)¦Ä .äŠp¡ p®9ÊHello Word! This is a plain file with several hard links BZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SYþ…/÷Àö€À@"«fß`@ T5#Ѩõ2i£Ò OJ6¡ ¦PŒH@åùÁjƒÔäì´—Þ@tFÉ9G7º‘Ï¿-]â"»H+F 4Bª¹%E©òÊËü]ÉáBCùübBZh91AY&SY Ò sÀõ€@@ª&Ì`@ P h2šSOI jyE‚T#Jµ«[0búéc˜Ø¢ Þ;zaw8‡¤R^’z‹¹"œ(H‡i?€BZh91AY&SYÞšºÜ sÀõ€@@ª&Ì`@ @ÓJzž£M4SOHP4 ™ $¨GÂæôÒŽßaBÂ¥DOC¿ ­O³v–Ç̓Ȁø»’)„†ôÕÖàHello Word! This is a plain file without hard link BZh91AY&SYãÄ¡º/÷Àõ€À@"«fß`@ T5#Ѩõ2hƒÒ OJ‘êhhГ$ rüYïj”ä­±ŠÞ@tÉ(Ã(Ï>¢g×v®Çñ]8+F 4Bª¹%âÔøg 2À?rE8PãÄ¡ºBZh91AY&SY;?Ïÿÿû0ÀÀ¼"€¿ÿÿwÀ¤P€°Qda(‘M ™¦$Þ¡44ÐÚ@ÃF‘‘¦Sz§¤$©§¤ôHz44õh4Ô¨Œ˜hô H4ÙˆR@C@46Á ØÐPËLÂ_U`äþ¢&Ë$L]³w€µ1CAB¾°Å*ŠbA’aD¼‹Çm‹lq!îÒö 9¿D€†’Å•[^Cä¨Fd&‹ï Èr™‚SAX#•K`¸á¶3iŽkA°ÊaE#Cm+tlÆ)° "@á"MHyÁƉıd…ÄÈ ûâTx]ÙS !$öã!k*]Ÿ’#Y H…³©žEA¡iBƒåšåýÚP@¡×ê/4ΦhàÙ=[µ¶mn£õÓ¸ÕLsÑŒd#õK}t â{xX©TÍFé\>ƒÊ¬JzA~²šëÁû:©Î_âîH§  ‡gà€‚Àdar-2.7.15/src/check/Old_format/archive_08.1.dar0000644000175000017500000000513714636066467016036 00000000000000{†W°ETT€€ †W°E080lN/A€C'­ýêw!D†W°E­ýêw!Fddirectory_1€€í€W/€W.€W.z€™÷­ýêw!Fbblock_device€€¼€W|ý€W|ý€W~u€C€Å5­ýêw!E€B&€system.posix_aclLcessr,dÿ@}D}\} Ü€­ýêw!r€ô°#G­ýêw!Fcchar_device€€¼€W}€W}€W~u€C€ÐP­ýêw!E€B&€system.posix_aclLcessr,dÿ@}D}\} Ü€­ýêw!r€ô°#G­ýêw!Flsymlink€€ÿ€W~ €W}¬€W~../plain text.txt€»f­ýêw!Ffsparse_file€€¤€W4€W.€W.€l€¹X€®ýêw!F€€­ýêw!R€­ýêw!Fmplain text 2.txt€>fplain text 2.txt€€¼€W|¬€W|®€W~u€C€9n€GšHello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!E€B&€system.posix_aclLcessr,dÿ@}D}\} Ü€­ýêw!r€ô°#G­ýêw!Fdempty_subdir€€ý€W}Å€W}Å€W~u€]z€¹u­ýêw!E€]&€system.posix_aclLcessr,PÿA\Ý Üuser.couH value of the EA€­ýêw!r€ùâ+­ýêw!Fz€z­ýêw!Fpnamed_pipe€€¼€W|ЀW|ЀW~u€C€”h­ýêw!E€B&€system.posix_aclLcessr,dÿ@}D}\} Ü€­ýêw!r€ô°#G­ýêw!Fslog€€¾€Whg€W ¡×€W~u€C€x¼­ýêw!E€@&€system.posix_aclLcessr,dÿ@}Ý\} Ü€­ýêw!r€ô°#G­ýêw!Fz€z­ýêw!Ffplain text.txt€€¼€W|Ÿ€W|q€W~u€]€3n€_¸Hello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!E€a&€system.posix_aclLcessr,PÿC ]D}\} Üuser.couH value of the EA€­ýêw!r€øâ+­ýêw!Fddirectory_2€€í€W}Å€W}Å€W}Åz€š­ýêw!Fbblock_device€€¤€W|ý€W|ý€W|ý€½­ýêw!Fcchar_device€€¤€W}€W}€W}€£Š­ýêw!Flsymlink€€ÿ€W}¬€W}¬€W}¬../plain text.txt€·f­ýêw!Fmplain text 2.txt€X€ë]­ýêw!Fdempty_subdir€€í€W}Å€W}Å€W}Åz€¿­ýêw!Fz€z­ýêw!Fpnamed_pipe€€¤€W|ЀW|ЀW|ЀOÍ­ýêw!Fslog€€¶€Whg€W ¡×€W}O€Bv­ýêw!Fz€z­ýêw!C€Ï†W@°Edroot€tØ€W~¡„ ddirectory_1*¢íN/q. bblock_device)¥¼^|ýò~uuCqžp ô°#Gcchar6þ}ð(þ@+ülsymlink+•ÿe m¬d ../plain text.txtfsparse_file+é¤y4(ƒ€Š} ”im(J 2 †>f/\/Xv|®+Zõ'Xa9i§†n„ 9}dempty_sub\3+Xn,}Åð¹ ]R• ùâ+zpnamed_pip1¥Ðð(J['Isyg+; hg ¡×,Íñ'Îzf-Ì/ÍŸqq+¾çø\a3iŸ„Ø %;ih)u 2/t (}Å,t .˜(v |ý/( .°(* };Ü ˆNÐ0Ü 5ÑX,d:L+/IÐ(Œ6*¶+Ì}Ozz„*j¤Ë€€5À080lN/A€€Ò/€ ÀTdar-2.7.15/src/check/Old_format/archive_08-1_crypto_bf_test.1.dar0000644000175000017500000003433714636066467021306 00000000000000{®W`îCTT€€ ®W`îC081zN/A0€T\µA6R Ñ$€ðLΚ½ê¶àäÄCûÆiWÙRʱþÄ—KTa¯v›‰¤yõ84ÔÖ0=xñyìÉ´¾OQ3—ž•âDÔSŽ$¡»` Rê—º!6˜Ô) \âiñOµ÷ñŒj°BóŒ*/mV^ٻنáV}÷ô¨Ž=WšnJ`N|¬ÿj²íWbÅ(®-n¼œ•žždÞ"I–ê^„ˆ{{ScÓu-pØÉÖAùìøÃPɪOŒ£2ž,ãÅ$Ü;Åü†V¢jÜ[s $Üë¨î¡šDxí_Gß '¯Ò¾ÔÎåÎÓÐãCM½µ oªÏ‡kgûâT¥E)Ú„wëw"ê3ÛFÆŽ†ìl—ð53'Ó6ëÕ×M"|Œêa óXÞð½k`=àQ^¹ÓŸXwˆW?¢àãPm 6F¡5ö‰ÿ4ÙR«I±$Ì“IöˆóuAƒíYd¼MºM<Æ.™­¿àNk›½öaÉ hTt3Aùh”ž¤Odvk ‰¥n; Þ©5¢š"`ï©¡)À’ëǰe¥Jš’½ (ŸÖ@®ô¨˜('Œu'–vë µzû~¢³––{POXA¬%戞 +Ï…†cµv^Ú”Ú›JÆcÑu¸XfÎ Kâá«J\Þr•‡÷ß{ªq]ívú‚êrM%bUz Ž5ÿ5$Îu{Ò;‡KFT!mùò3óù(ñ¿_C³ûô¨2~ö}â@¥ƒ»Ô*žpð)¿ÃMÏpÝ‚CwÐ{Àã¹/rÉ1¼¯©;GŽ{¹¯ýÖ”ÃBžFÑFѵ²Ws¬D/¼ëgõ?DáùµÚÝÉÄ¢:é›Åè((¥´¡<¹^hñ‚‰6d¬öŽŸÑ‰ªøDãWõH†0/ö;¼9—fCÑÛ N õÔPïòå~âû¤ƒà[;àOEŸ¸S¸îàl;"ƒO¿¤ö&¸X ®gg(¾y=×Ꙇñà6ˆ¢E,’®Ë[?±L˜×Ò²é±0<ïÖæÖ#Ò›S†ppY(òV}Ö¡^Žå‘*ŸØö§ñ=ö„¶u»íæFPM8ÓàFvæÕÇÅû_òËm‚èØ¬Ü~Ü'ý±ú·mA$—<ì¸,c™?<0;g\—g‡ß›q\[a’æBïX4Y1ÂwÔ“IACŠLœxÀMsBØXÎFÖýÁŸ¸¤T@›É4H¨?³q’‚ hS߯aUÌ ÌéuÓ9ÇÝ$ÿGOqûä'I`žºÄeÔ¸E÷¥ÛDRUàõÉÑšÄ-²lqo(¾_ôO947‡îCÚ$¹y³Qž˜å†§7ÿÈLtýç°ýD⸌ˆæ9BÚ‡¥x÷6[p~,¯¹¥D¼ öÁÔ8¥jÿ€ž*½_þ‡Ë½Á°§ï1”y©†ßv •“‚/@HÐ*ž§)ÇlG¢yeˆ½\ÿ”òeÛ‡8EÞ«3q ŽA®¼W6Œ±””¾ˆr2êÂFžÄB9ÄçÌÔø®«5Õsãôqƒµ…„5óÙŒ!¿“ÝÛ7ÆÝ™ÌAFYÛÇFƒg39jÎû6Ü”éM wÚ¨~òßXA6Íîsú‹®@´ÿÙ`$^§ªÖo‘QÁpÃ4Ÿ6)²„IU”Ûu|Z’P¯PÆŒ<5Íèa'¤I·huí EÞªXg!Œ,¤ ‘P†™%ev:UªÎÁŽõ´ÈCÛ²/Á½áq‹_Å%Ya¹€Ê$‚3ÓݺÙSœÒ”Nÿ=¬¤ 6šÃ~‡| p7ÓA<­*LHbr懊ù ¶¶mÕ+É–3`À ZÒ8l ‡TéÝLg¡(²‡Ë‰ c;6¹ÖKWýIbqnçß T€æ¹\Óëóâù)Fch¸W¾¢-?±k˜gÔPÜ·-!޾öâŒÄç𠀄> ¥¬räÁÙ8aNPEÕëã½ô€y…H®gR1~q‡Jr<ì÷ð3ÇÇ,!?s‚«‡ö‹\ÿÂ×G\" 67#7Öø²lÂ1õå—0IJ´£ $•nø“T€%^Ó°¾Á¢Õ á³ÑúÀ7öÛŒ=ŽÆ¨†²»¿¶Ó‰ê°…¦áÜ1Ïw`ßS\lP©€°Y~£SN’ÌÃ.¢R÷©S3»ýU­òMܯ}ñ·Á * ?®Á}•²ÄœuàÞµR ªÆêpž¬ài¿’ï²÷ÝCͽgÐÈÓâ!½t”šíˉ€ƒç•>}‡Ó‰¡{¤‚Ä™âCŸß †Wªˆ²‘·Ûvz~žÐÒ$‰¸ˆß¬©0ˆÅÓÝÿjûê™ô:Zi‰¼¯Kèäd, öÆtŸUô—ºû*,T•(ˆú’èr`®wi»¿Æ±¾öšA·0÷NØð¦KZ‘[‘ý 0LûeÅé/t‡žFÑóõ¢¤–Ýj9Ö/æ Ä»„m'øàRY(·@pðøSb… ½@#mŠÚ–Jç?õýœk¨Ô¡@ƒôûAês3NzøQ-à‘Bsì˺‘°2¢'"²`ÙŸ|­‚mçkÕuàÞ<³ºo­Âv¦ å;·˜ VýE¥öÿúÇ×'Ïyá©/‰&ãé\ÿYrMrk×o_ã@’1Cä}ÈàíÙÛ+ó›¼Ö8½ÑÈŠŽ…­Õp7˜át€6H§Æ=&–Œ`ÃÊgGŸÁ9¯Õ0G‰$ìï_¹™PzlRÄéPõ;<Û cÇ*ìûCK?¿-I0ìÚž=ek‚ü—í‚DzQ7y–ÀÊK+ÍæA+¢µ½ýUPìÇ‹Q)!ÇŸv÷‰¼iy Í;¥ÒÄQ/²blL²ø ™i^æì¾<°Iسܒt ¥ATT0‰È»!ÀUõ68'\E†_¿´¸òÝË@ ‡“bóúí 8Ǩ?—Ø£H傾®³BÛsÓ1#ÈýR9òìß:–I+é^ùô ÀJü({0Ú_Ûv>g—Úbå¤íö ùeujržê7¯ ÄëpsJ,&ÖÖÞãŸMá\rA{9;éy ŒÙMörl«3‰kî‰_ЛÛc )®n óéç;%]uÚò¦CñÀÅ*\´Û\5¶û€ç,»xÕ58¨Ü÷Ï=Ø,º07ƒ(ÎÔbLÚ˜t •}R-ß-`h,K5Õþ³(ò³¨¬ó3ä¯~ù#¡Ý;/~ãÉ2fxªn¡H£xâ³[ðò¢ Ó+< ~ükßOÍÊÇf;Ï¥Aí'Äk7d…ÎY"óˆÑ¡i gPFþÁ¥LÒ~ùhÌÄßÒ»Ñuõë¯ó}T ò( 6ÂëHEI°¿ÏžKm¡ªÍ’n*Ö‚ §‡H²_‡¹k%NPâP¹9:å1`g‰…Ù‹~údÂðŸU6Få’ÆþÀ”±køFfÀ0Äóør×CÇŽÚDÀ/Ç5Ag¯hV1¹•CfÏ4£³_,v6´’¦‚¸‡˜7éHÔk¯D7–ö¯®ŽÄ×n²£ê¡2¾è­ƒ"˜ @·ór×Lb¦úÅ‘ã¯†×¼Ó Ú°ëq°Q`Ê|ÒÚ]ªˆ$°ê9N+ÍXÀ^TWcdæ$iëÈû$N`Í ™#5•ŠÆÆàºÆú’îQðÉwäTÙ¤Û€±ˆUV“óØñ½©"c@ª2d€u\ôLuN·$ÒÝ?ö5".L]곪4¤M^†Íj ºk`Âxo~`UPÝÝ“Iè“$ŒÝ'žaèqÞÎ=_å̸eÃtŽ”mBƒ>oiˆíFù&UäÑé'iç ó—Ö%C»¾Ñ ÷qr1#/Q."<œÆ%cÂÜåE4SÑN£§·.\dþ#“F9”µ÷J@]6*gI/4ºiÉû´iúˆÜÚÀFì‚ÜÁWÿ‹:JcÆùu¤üü.J™ÎÞ~3WEþ¢ QB+§þá*©¨Õ6²UôeAmKôÔ†·AªH¤³÷¶?Þ:¤}únŒ¸û  ]ùÙÂC)R"R¤Ç’ÁÏng!¹>FêèÛ‡s~Ȱ€ á¹¢aÞÀ¥úª!*¤ÝÏ )Å«\bÊ@ÛŒÐp5äc´©¾àjB3Q ì׳i±L‡pmóÃôvJaúšþ@%Ä``GñìnÕb—=]-P¡ e®z D Æ~a¿ŽD‘Ë‹4²;½^¹C0ܪo-Æ ?ÀÈGGU;g„Ê"½ (óL7¬F¨Ñ„‘S,Þí…¦-Áç#²ùêd;7¹^*’Öš¯Î3蚈péþË÷2kÜ#“ íñŸîx#ø8!.ùyíSr·¼^f9Õ­÷ü¬M¤’èêéŸî ¯r‚ܯ*è°-ZëÝùòðÛküe-Cüoady´LU‹Èk ³—µ¡GÀ²yédïÙ´®6ƒ uØDkP¿¶Õ¾ Ä·©G „4ÿü'mF¥¿$qè܇±›©vIœHûïƒ)7?w/É‹è³Á[9é„H ·ñ¦Ïä¬ ZqÐ3´ØÕ.S‘@:ÍAy›NZˆþ†ò"³í•r+m$†ÖS WÒœÕÅ7.„²à9 Ñû%oÿ.–ÛpŽSf…ZϨ®Î>Ok㻕>|Œ2J,N~Êþ¥%&AwùžlÞœ#é–šüÓ2Í¥é Tµ¤I{Ï4€a͇šp«w†¦lÛÞ±ªTa»< ßÞF„†P\È ´¨¼C®y¹š^ÑiTýŽÊÖ\fáܲ¿e^<Ý?¥ãIÞäZcU—ߎz¶:±l¢¾ÃÖøƒ?ƒ¡a5w"ðCO<1ߥS{ADR”ìwMf :Žæ%‹_lf˜å‰¶]íþò¥‹ñÈnêtp÷²>ë§]üºå¢LÁ›6ûÏûOƒHÊî´Aú^eEGE‰L€ÌIÓÌT¼MV‘î÷=ÔC‚ÖØ‘u¬Xé)°ô&wǰN­l]#™ 4Ì´åå=|×8w`µÁù)C ”dtø© XÐy©.+ ôa¿¼Ë®7GKz¤Tq~Za\­.¬åq:69>ƒœ¿üdI »2Ú¬Ó¢¼uqÍ÷1Ëä fâXT»˜iN%föÇÏÖ¶ÉE #YÞDM_3QÒ€hœ1g±Æ©('÷@[c9ú×éFiÅœíÁ~!G_nñÅšP*C ¾ËöR„å ÇLÁà71¹â›Y&Íã™i›´§µüñt„ÑÔY815ÇY˜ÏeÏoÒ0Z]ÐÛïSÔ«Rí¦¸µ ѺV<ûJ¹@E…—AÀ¨£Òl ä—RX ÎH§éÙç|ަ€õµ]›éëºÇR•}ñ×(_C‹ýW…[ëç› Œˆ°Z²µ2s’‚ÄžSþïŽûÞ¢Çag_é«úº@±i@Uà¤QR@‰Ç¸u^Ñì“#GÙø~ÀöI†Ï¸wØ09í9/ÞU诹çÔBè;‡æU»Š}Qo’m’kÈ·±w-Áé~`ïX ¾×+"³Ú@YM~õ,ì3t»qT’‰9‡Ø9lųY‹0·ŒªU¥)9àP¥*ûè,ÞUùáV–.Ü ŸœÐéeyäð[&9)Àx’ë-“ß½ :oÙé³[_·>?¹+ïªT"´”<§Ó cmEXÇ]ÎôÚäÄ]ìÞŽ)ŒC˜P<ýøTQz³=  l?,ÔLÃѲHFÐê€S6ÊÄvx¾Å£{° X¥ô¨è¤Ïq±¦q?ÓcÕ['³1jW(;¬—»g,A°Eµšu¥ÀžÖë±UÛ`!.Þ›6¤á©Têb¿ÃRr¢Ù 쾆ê·y{ÞP½>M£çÒT뤽ÄF"Ñ¡\é ¨\¬‰[@°îÜGï+ší\ütÇfÛäÇXp.š–ù®äíE$íèZ(š¶ÐÃj‰˜Æ(6¶¨ê¢Ííå<Ê"r¸”PH‘?C6ËÀ›H¦KÒQ‚·4`sE)Šë$™:•%'ŠƒÜsçó€»-JU]Ÿ×rR†Ÿ—¢Äœô{°ç´á¶WiÆh<ßu,¬\°×ëzRmâE€‹•a÷ c£3ÊzÈ!6«ÍýSv¨Šñou 7UÏqXÚ…à¢qý /atjJnìæ%-&O¶Î–È®P%MOô•`ÖÛŸ¡}ˆ{"/®±£BèGưÈ‹ l@BUã{hꦸgÅà¼#¹ÂÈýpÀø¹Ró1ª„¶¹3…˜f(ðþÛ¹žN E)º¦!(*Ì™ÿ=‰:¹püãÁ¯Ó½iíq}QªOX±‘åÈØ¾3Y½ÉÃራW(à ß&ËbbÂÅ,_iàˆƒ·9I0ítZó[wIЂÔ@ç+Ýh)§š§Aå¹ï22Î#,w]¸Q±²Äõ|ÏS¿ð~Û&{ gÝgZd<ɸ;ÂÁ'Ûz±#ù;F~ó¨§Ï¥H½yóª×I©Á¿s@ ýôýv¹('ŒÎtã0Àz±PÖù³ÏVIö—•†2.¨•Ó9Ì)…NÞŒ½›¢t±ÖvYAN?áeÊŽw5x<ÓÚå7xðÞ’Î;ƒc—î+²âÔú÷)¢8—t_è\ÕÄòC³Á‡C¨läc®ßx½¢ì·å»l°2xMöÊǼ¦þX@Ïk¥ùÓ ÜÿhùñÎeÏ’¿ZDåÅ]«éúIŽŒÞF™:à\a ”Ãøþ÷ ]Z]”¹ ȃ@_`=Á ë¶Çðej ,¥é™¾]ôkuäùP}´*8 ²qŸw{¾ÍárÊBͶbÄôÇ4¡úEóÍRl`hªôýÄÈë:M8Á`Ôà0‘`ž`à=žù¼Ýî¤õ/-t1-ìî¢þ,¾[ß(—Z} _ÊY»Ä§êë¾UUYlieln>ÆÚ¹±‘LKžºqŽ€äixz«ãíÝuj}(åkw {ÿ3¿`ÙzæÄé5 yjî¦˜Òø¤é÷×5¶Íÿ1pë3<ňK¾)H²€z[r‡ŠÛ`cÌöïT¾|;Kš3@†l¢ sRÏuо¾[éþ'cøºéë¹¼ã¥7àÏÝb®X@ò\êßp.ßÍ3Ðû‡ívz Ùñù\ÏàgÛ˜CöA± ë™¸Jî‘QfDØqùŽnÌí!ö ¶!»X¸n]ùܯ')€ m'Ó!á/ÎedL'tÊ]CàO放V¶Qÿ@|!ñoT" äü÷ç<ÇhWD·õ¡9~° Ð{ØÁðå?›ÈòÛ;Ñ@‹žNV@ÙNê‘2ïò„#q¹@îÄRÏUrsÎ –Èž@`š—é°¬Š*ïºï5/»ýSôïnl‹¹ðøP”,Äñ$Ï>|L Êpuk?ì ×k*—°Rbæ´AYžu5cƒÈ“ –²î×;e]¹6œa?NOÔ:Œª2›¦ ¢s·¥5ÜÎûôõÇ×^A¼Åx€‡á‰"{?±»nðâº{oŸsÕž£MšŒê§…ÝM_÷œ–)éF‹õÎN–dl%iÕ•pø`îž+„)N“B2Ò0C]h1q ,7™°un؆D\N9óþNÛÌa´îK@Q¿Ëz¯NÖíEçôü=ófÒf}$Þ€4 vsXÊÜ '×5V„–Ên%,nPq0Œ«Ø³¼É„ ¤ïèc¿u3€øÀ·X—1èû]øŽGF%¾V/4¾¼.GoºÜ)H§pÖgDžÊTŽN£N)k¯üŽOLèÍt"|D€`,C<õ ‚§*KA¬ÞåP¹Y{ #A.‡7³«œÈô ÞnuíÐgœÐù NgqEZ¾!óC@ çË—¢hd°ä´øƒâ‚¥F€Î°@›êí܃ö–mc àÉHB’$$æn›XZ–ì+yºßåàðFä¸qÑ…f´ÞÉf' bF%O9`WÊ^Æy.޾í¤Í¼#<ñ$£q™]LõØ·Ø)–¡ì9žÁ½£ŠÎñEø2%‹–ISu±hÅø§…ä—Ȳ»H€KkÝÕÊæu.÷GÕ§ÚG/¶“¬™Ãô¢7|}g´ .‘æ îí#-¼Ü@ˆõr:›ûG@­Q£t˜.S¬0ÏDüuæÑ C£l×¹PY¶ÃzJ¼©­€Š‚Ÿc}}H§þø }æ$ îŸWȘ[&Û £‚kVI­Ó Á]-Ú;Í ™äß“"ΡU³X¾nRzpÄóñ†$¥Ì7'°ÐÂе{üaÐ4ÆX J)’ë{žc¨lå§ô­æF.Z♑&ž!Ö#š$aûÙrÃâŒBü,ŸF眀MÊÉè`ð ó8x ±}Ð…²± jäB~M¤v bÙ¨çqqno•ˆ(.{“³Íz/˜‘9Åú_Õ%º£¬P ü]÷»SI:hûН_qkLª·EÃ÷çQŠlÑOô¼¨˜!Ó™RѶã'6ã©Ü™Š:™‹ìïšr³a0§”ÙÍx•2éµÔ–âØIF„¹˜‰à²…x¹¢?ó `}ܶø KÜs]npfâ>]á¡mSHÑ`ŠÓõÎvxóµíA›Õ—Ÿmz3 âóky§a|‘‰fhůß*F<è Зê4ÞË0Ÿÿ^ËŸ =×Ó·Âa+²§ÓlD‘¡ó~êág¸-vÊF \]mO83nþy#"9…FJ.egùe;=‹Ôþ4|¾?y“=¼h\Áõº×p!b·MѪ91úò†IÝ00„Ć b˜D8qRèŠ@+ÝÇ%¿¢å}Q8vê~ö_ùLôÐåÕ A¢øàs@³8‡¬2éøˆY‡—³ÇÍÄh_þÕ!Ûдs¼^é§¼8(Åàõ+8뎂»c€Ÿp»„A%5¨CGwðh.“V*„ } Ûé; ºzHÚ€Œ²ëå©cúûÆâ‹Õtnê_@âÊý®·w¯y=ùIñ<ôÝ›Í2¸,võƒ$Òí´Æ¤`=M$7©gQ#^T|þ³›ÑNi¹„\ <–9ùß_´¨JÉnoƒ«ÒƒG¢ÊqГÃ'ü騱Žg³h—Á˜ªjE~ -ì«õ«Œ­ÖûhZ MÂ9‹d°³#J®Œ#1“«V’›JÕn£ ºTüÌØç´×2¸6ÿ·kA@££4\Œ<8V}t}èÛ DãnpKgCÖ>/1˜ ¿öMÓPìx“صÍ+gagƒ9%^EMí@^Ý£”€NQàrå© ”U|Ý¿÷S›AÙÕºŠ<Ëú@D·g›øÌ·JÐq¸L÷×›#Ù}ù;V×Ò{¹A['»€†öÓ© QeÛžE±¿X€ãqiàXd”•(TRÖzjÆscømÁ Ù‹´De»ËߪÍÔøHÈ¥lÝÈÊ Ì?$r?})fm|EÁgúÞ´V3,½½ÿ5å>\À²–È›2¾ÕÒ8Rx‘ ž¾Ž ••ÃrGù÷m»tFG{‚]1i×EG©]P©\øì€ÀÚV);'¶9V ºñ2: >ÏÌAÏ )+ͤ–9»nÙ}þ­S—-sNt*ÖÁ‹ºB£ÿ[°2V.îî »íbÎÃ^ÃHßÜQa„©éÿ“†GŽ,´t§µixTò‰ÊªFªˆH–]†#•ÇNr(¥Ô÷}(m0|5s=(Áhܘn×õØ0‘1Á·ˆª„®G¸êŸŒX0+ߤ5¯_‚GÂwä%[©a>qŽ„'½Ï†%DÇZµHd‡ò©ÌO;;§±ÒM)¦^̾ëƒèU  Í x°O^#þJŸ4Bè„—/ânXcÕ-’í9¹1'›zA­IüúvÞ'œ¢±)5³XÀ`'ºæÐ‚‚ìÀfQr!§¦% ½úÚ(˜†ª8ÅYt¾ü%hú–‘|J´´¿àmêœÜQG8G·yë×ÄmJR-ôû8!#Ó'KOf6±n4~!Ö~KT M¯è¼-5ÞÚí/À¾ ôä…ð†×>ö‘B2› sŽeÛäÍ Ê«§ñ7Ò.Ù2¹ ¯=îÂRæ!@€±œoM Çîž›ÕCTù>F[þðâ3^ž]NÍÛxÝ€²5»b¿ûêfÐû^ìH‚®”\ÓƒRy‘Oz½2zçz ìLkkæÜ?Vm„È« /²+¾§bÁb,iX.*t^^+Y!åc}yš8pÜ:v¶oïM™ªÞ>Êïˆlåù%ÞŸ"zà'ÑY¥“g. H<% žCýNÏm?H¦Cö™‰[Yþ½½™QЃvÞœ¤¢žh uXxKì‘4Zˆ–_ìɆêâaö™—÷ÀQy±\/Íîú9b¤åÓ¨Jr–UM>iÇ7NóV¦ÒÄ9ðKžÝ'Ìv`öÓg/“rl=¥Ù-*ßè`ßá”w%]R;µª$_)Œ¿¿qy&/0DÑ^;—kPH’¬G.^8›¢[÷J¿Nú¿ç™:í¯°-ÆÚ㸑üN›|$NcñÐëiô&Ö „«"â±õ‹Iù|{.àƒ|6ì3éóÈf4@ÏÙŸ»dÈe¾ï~ÿ+$WQÝôtEV™zëŠ#UÀ}Ûºãu˜× $”×9o7o=?Eþ´þ9†`®H4•\(ž_(n)w-_'töÇJ}pMrçq6]yHCµa¸º60 žmiëP‚ŠòˆÊ¯ÇÆ †::p^S¿UðÝú £¶ itÂkŒÆoÒ–k5õôŠx…]îáX·:ŸÀ,çUUèÃà75û“¹PƒæÇ¥ o©{GGÛ,¼¥±óq¬ZAþYT‹q%ü­©Ä´Œý¯°xªÓ›¤#8…±ìE×2Ë/‘+a¼‰_´èK²åeÿzÊZ˜—4÷•ŽÑÚ·|Þ€²á‚~ˆ…%dnl¹[˜{ÕÍÃ0ä Õwº=H«»2ªº£“tº¢NßwWDh±pïÛ¦[1TÉ€g© 'oyÝE/ç­i ÄJž¡ªæ[n–$ÊgÏ^s¥FÌ»°*½Ñ’ŒÊÀ÷Ëuaò–1Ò7—â(TxÆ*~E¦e½5‘|ý˜(Ùgï/ç~øQ7~Õc)[Émâfœò½ìd»J±'Ÿx¤¶1÷¢œs¸Õ÷EӠý,Ÿ •Õ&Ôg“؈ô°"H5ÆHùÔ‡Ä[ê$ t/ÌiIsÿJP· j²°¥ˆ°€;“EÒ;ó¸µè»äÕ>Ž·*í?»á1ò0T9rþæ˜ñPWd¥EÖ]ØÊÇ,O-”JÌ6-]¦Ö.i9à“6:‚yH”Ûg¦/Ò'5ˆÂÛšåƒli+7“¾wáðYÚD†Ö£ÞÃA!!yÅñ ¥‰ßàÓô q í¸ÇÜö`ã¢iâFrìää”Ï7s§ÄéÈpÐTÍý Å.š ºÖ9%•Ù¥8|".îØ˜£†c×€uI ¹(œËw· Ía¾ìq:¬nO¹&þRñKµNÿ¬6F²Zöþ Òl»ý»§ÞÇ1„’§ÞÍ…ü "‘‹$8ªž]fQÙÛæcjV ÉL2 hè7ã5.8=¶Ði»¨ô(Ö|dû/ËÛÀéhç8EuPŽei¸¶)Ì3žËX¤mǃ=m›ðdñŠÂÛ¡•ø6hß2±„w ­ ^¦¹ÛÒANÊcWƒsI×6Z7Í“m_»8Ÿª0ÜO€åao¼ 3©;EeLšÜ(h™phôùœ‘„ƒs¥¡I&g=ŠôÙÿY~v²;A¶ÂÅè/z3°þŠ5ˆ*Oå.Ç—y£`ûäm‡J¤R˜[:¾xïÞùáR@%O·CêjÞ/9°?Ë ¼ãÍjÕ¤|Bø¹1 Uµ·Hï§„Ëž•GJØ!RQ–MV,ùmy4\MTàÚ»¾Ï¡¼™¨Ys½Ÿé²sÚw¹ï8(‚jÌ|VoHßì‹O!îÍõ„9@oÕ#ª>íæI›, >¢Ö£7×ú!rõÔˆ4›u {mŸNœóþVÿÊP ËŸûâÓ¶øÃè ¡”®aÓÀBõÇý,öŽü<^Sýä}½—5m,;¤üc¨™Ùð(xÊÔž,—f¡VaVä…4Dl â$qúí0¨I—G\9ø¬Í`ou“4tÆÂoe‰M¤K›ýÇó]*/ªÕ+j[µZ2ÐÎ3?UĚܷ­_DBöÖ&?êÿFV ²°x#Y{Žÿ7Úü{]{¨6ÉË/ó!½Ï(©I~·JF]ðÇþ‚œzø™ÐþŽHJÎî6jÁ ÎÎÊöEÁàÃÁz«å ~2ZÚÅ猫¼Â\Ç5yOÓ0ºØ£c îRÕpv snKËcÁN3‹k±rZDVØÒÍc浬ߜ؃NK£ x1±Ϩ’¯o«sÑÍÙ‹±ÇÓ–ã”zR35ÙÜòž %àƒ4e ºcý9šSظ­TÁQ<¶”@U0µB ‘eÇÂŒ!·ºëÀ¸×ß×¢x‰Chw/,w¼R0©ïwm&8Pn¶A'ïõaå¿ ìE+#ÞJ¶g:kjŸòNAt¦ÜL„7F©È=ŠQ-:PRôE#@½Pu`û¥U>#ß}Ña0¥‰Lþ­oöóEâ»õÕ…Ú‚ž”£‡å9[?ÔWusy*bLÏ\×îf& zÂ.Dœ‡›ù¾]ÑtúGgFpà©Äóz«¹á>B >ïZû«>„9¤¯F Ð––:Î,¾Ï šÉ ñÀ`‘c±ƒŠÊÖì™'q, ŸYöGì‡- 1B¬iDî4£¹âo 5Û¢.ÈŸi½Ï¸~ÜêËS>H±µyoØ¡%4þnÞM`ãEãÕ—ââ/=¸%ñâÝ>MÊðÐ…*B1•» R”&Ðm=ŒmmTO’ÈÒçøáü&n‹J-r#G'¯ ­´ï”òá2c²Ò1Cp2®Û58†ß®hæj’V÷Y¡ÄT4^Lüšsaí¢Éxüïúc…>I#Ôé ®°9úÕx/nUçù+žqY½ëã1‹Ûfãï͆d Tc6™~rÅÛB=.”~’qµÔ½çyŠá›ÉÖ¶eíÝpàâ‰Øc3”2Íñ‘ÛÔÔ9Ÿ1— 8BPDCT½˜žtd‰a%ƒŽèîÜA´g5qŠ÷H>ã´&.Yº‚ËMðTœÔ,×øûG!ƒ=r-ý%ÆÌKÏ XõÄØÃíè|¸–ÌÝGVÁ³@x„ߺ>Åz9Pÿóöá½C ÔeKÍ/B˜›Ê/â•‹6QIÔì†Y¡ø 5PýÕƒ) ò `¹¼éîÑÉì9•JnUß×¹ð™4›&°¹aEÈHÙß/é\ÍŒ¬¿9Øzè|[ÒÔ ·i˜¨oŽ9:,½éqSÕñ]—J·V#ï&ˆ7‘äÿÝP˜†àÐ;ŒáÀ¹)øÖÂé öz×gr¶ÚïA\¯`áM×ê§´^ÜÀZ›ýï Ljm3¹y6þHϲ¬¸°ªš–¶*IùWÝáÆm…40øÎtÜN„¸›­ïìáØ²ŸG·%šO«I,•3îÍŽºÜ­“«ÝÄpï¢k7=î5?Æ»á£VèDø¥l£ÅêOiwU+°—)°5~¼ûþÀ¡\aÞ$…áà þœàöõ_ï\í/¦:áž×!øN2±Æ¿êpš÷.{?Cò­vÿ–ã™·IÐG\¾ü@¢ªwUæŠbw]úwÞðPŠ™Ž`I«‚UÕÔ+ÿkððôŠj]÷)‘ôĪýI~è„•Pa&~OÆt¶ÃÐê9‰›:ÁÂåÝì!°L«ùD"Eéæ|hXL¦© §à},)ÜGKÂüЦ֤‡èJ„šC# ÜL6[mÿµj…ˆ@z—áQ[üªøc¶ \$;êy^‚ín£º×šN8“Ö…Vî™ is­UÂÞÈ•Èå“ëC.FæJ_ý‰XÏb~y2oÉíDdl;à åÕ´õ®NìgŽ’Wêóý%N ›DÝi[üÞk 8)_\PÅÌTœäå<ÌøX­Àù/@Hâõ~¯êM7cu†þ+o² „_(ƒ#€»-â 6a,tL[œ…1!¥Õ9ÑÖ}Ó [¤< cb ¦¡æéÇß]ñÇ~õ¥@ä½Y§³õq©ê,õ)Ê<éP, FMóº&~À­aUÀJyfžÍ hÁ5÷⬯iú‹vXBLÿÅÁÃZ;Gê§Å]K§À½ØLß\9Šà« G9ówÂMßð Žws°¾Q;{†o.½À©§kç7á6î1ûŸ¬³ m~ÁAØ/Ë((¸Z%¹^¿•)´Ø5-6ÊŠ­[…¶M"áÞâ-J÷DË 5òÿ”Ô_lJUym‹žh6çhPæTø7Ö£lµ ë1pß—]Ó½B‰›çð`<Ñ›òªD'ãåõAdÁF¢oô TŒ D#æhk5qq1¯ïP.u­WîQ’å?Èì/± ‚Ä(Þ4 õ‡>—†€üÿid¤…©ƒâa&5É]Û±©:½?mXè¹çS—•½9æòi†âó&Íý¿_ùY?)u‚}Ø'^,|ZvJΉ¿›;‡u>ù²0ÄWp[Ñ)­ …°íÕ®ö¦¸x>èéfaU.Ê?ß¡Úuhé^àŠ8@µfÆfÃA·I1¸§¡Õ³vEñlâ÷Q&ëòk,Â)oM£òù:‘—F§·ÎúÀQ £íbde¶÷Ñ0†y¿ÁÿÞù–©Ùòhû|S¸nA-ïŸ(¢Ûb|!mŒƒŠ ³@£TÂÝñ‡UùŽCx8–¦LÆ,nëž)'Œ»yF ã«O¸îleϠϯ2º»5PUÀ}ˆP=dEÊ›T¡5ŰëÔŠæÁ/’_~dßã…%qL-3ø( •ÛÁ¿å 8F#æ[^ èý%%/ŸÆ '¿‰’£Ø*åpBN—γãØ@Í beTÌÉâý›MA@#p€y­´UºA>ÿû»‰èRÍ *ëžgcµ%NÃ-GDÕG’–ÄÈH“öå1‘ÊbbDN²³ßU²thÝesºÆ‘ŸW¨Fþ>´­ž÷ü³Y$ö0úï¤0{³î¼Ù O×FDoÕÕZÍ(üY‘©]زîôvGŸœžE g¤¥§Ë»h‹ò Ûzì^ë~§…@svPq~M{9ùåÀŠ\"˜gu{ †_ö%½u>ìÈî«Ã‚^¡Š¨|Œ!5ž¼ý A°(Ïí8N)*»bV@Ú)¶L?‹^ž>ñ×ÿ‘>¨Ï>ø67™ðCŽ nÑë^œôù :bò¶N¹ãëSt¤œjVº'¥×'_lq¯ƒ ó?; >•_»Š¤)WšLi‘ŸNJ/ÚÿVHøüÖyv6Ö;¸nMQWomtý­ºs£,EBŒ@ç±35Jõa׸wXÁ!ÂØùªJìmßÙ÷i0/ý¥Hö-U G›„s8öÒ±?Lˆtm<}"›ãL‹Ôqà 2w‘„ö4… £óMkzÓαx½-š}#ܰKF:Ò®ÓìÒ1OÜšôyÕË ¬‹ŠXžáÈLjcÈ´Xq#&ä;ï©-å÷'^ë›>DYk¼éý­¤·*ýTŸ¡¶‡óSÝ| Ò¦y^éÞmë$‡­¹h`·lð[Á¹Æ¬çç¿X¨Ep˜@÷7}~jƒ5ãÃQAtRÓÆöÖ@ÑVq KûRì; Óeç|fýöÄ¿ëõ( hlºâ<]…™»,Á ”F豆ìÀjµ¶ÆÀú-†ÐÿéB#)COwd¶V{i|×菱âd­hWÿ§ÁðhÒ>_©àÍ׋R È!þ ¶¨…ÉÖ'O‘µ´—‘¤í>³Æ~i°71O”òzÓ×ÕRS­ÀMEƒÙ)h?(×ÙƒïÍ4Í” Bº£Tÿ‹ŽÎ$—-ßJ‚¨·tLµÙƒšN)¦½ ¬…â÷¼›òôæ0žã‡«'?½¡ºx¯fèPéõlkö…%Gÿ2í¬‘½" úüG|kgÐøü» ñk(ˆó<¬Eø \¬Ð>ˆñ‚Ê¥aˆhÓ(úù Nåb,“Q§?Jàÿý·†Œ/¼}/ÿœ¶2+w[§@OŽ»€Ã|ép{ɶ"ÏÞÔrÝi§œY%MNfé$7¯Øþ¶aôïF!5÷NM9 ²²LRôÓ+ÉÚmï/Êq·üª ’›QÏG-ü+µ±‚z4¸Ô.oFh“ñ÷™ùÑ^î髎_ôY¶Dü»Ž£$–j—c#€÷:åoIÜá:êí¾áí¯p9RÙY¼ã©a±lE´WáR6P”}Ê™tÝ8VÛ6þÎ]Ò`Ð*g†Sw]øÙfÄgîá%žÕ¯ Ä´‹‘èaáFãÕa‘ÓŽ¿‡lmH™‚šÐ˺v€ˆ æ{FxŸçDrR×ÌQñúoõ¹oˆ']iõ®ÄtJƒ™}ÛßòŠšŠ°—t×»±…]·ÉßscCÁ:H‡®w|‡ÇF…}9Ucé}v–¹LÓ|üQaó°êùó‘Á¼-‚c¶,f݈ÿgmÓw¹otK¸)õ|˜…OåÌà º°vª—·$Ò™;ˆõÑÐÏ K$(¶6dÙÏ>Sú¹fÿ}¨øÓH¶‘ Tý[]ãS…^†lxc¾,G­èÞæà“›3ÝÎZƒ¿‚MY2\kIëÉÚÔMì~]ûaˆ0çFdÕ­ZeÂ'¾rà{ôõÙ]Ÿ¦%hµÖ÷>·ƒÍ^1l‰ê'Ôÿ‹ö†/ÍCõîœ081zN/A8€€Å€8™ÀTdar-2.7.15/src/check/Old_format/archive_03.1.dar0000644000175000017500000000102414636066467016020 00000000000000{#‘WYTN03yN/ABZh91AY&SY8Wå@À 0Ì)¦Ä .äŠp¡ p®9ÊHello Word! This is a plain file with several hard links Hello Word! This is a plain file without hard link BZh91AY&SYÌeg]¼ÿù0@À½"€¿ïÿwÀ¤@€°2ÈÂP”ž§¢oTôbžIõ ÈÙLž‘êm5‘I0 €L0Tˆ4ÄzQê44ÐF›P„è`¡Ì„¤1ц„‚æqÓÏÍA°Ÿpä°‡fBFÐmÖÇ`•Ve6q=Eg œè8Ü¡Üñ¨‹¬žeãÙ G©«ú¤¦ÂpFH$+FÑPrK¢›lbÉGš ˆ$¢üêeqóY7ˆ1ˆ(H[& °¡bȈK#ˆB)„'œ–>H ‹üÒ|‘›a1ŠLÏ"Þ`yŒû P3¡¡ ‰IBdŒ8’ÞÁjo.S-UuÙ\t–ï²Òš ]¸fÜ3EÙâházþްr«"q8{ N ¾ ¹"œ(Hf2³®€€¢Àdar-2.7.15/src/check/Old_format/archive_10.1.dar0000644000175000017500000000525614636066467016031 00000000000000{øˆv^ª+TT€€ øˆv^ª+0:0xN/A€W%­ýêw!Døˆv^ª+­ýêw!FDdirectory_1€€íu€^vˆ€È4u€W.€?u€^vˆL€¡z€,»­ýêw!FSlog€€¾u€Whg€þu€W ¡×€u€^vˆL€‘b€å­ýêw!FCchar_device€€¼u€W}€‡—u€W}€‡—u€^vˆL€b‚€Z†­ýêw!Fmplain text 2.txt€>fplain text 2.txt€€¼u€^vˆÏ€‘u€^vˆÏ€‘u€^vˆÏ€‘€w€9n€ü.Hello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!Eý7zXZi"Þ6!ÏXÌàL]@0&ú+‡%r´’¡H™nË44i2ÿ¤=´Î1a öÁ¤2Ö.jaÝPI£[ZàsÒÒd0 ‹YZ­ýêw!r€0O­ýêw!FLsymlink€€ÿu€W~ € u€W}¬€‹u€^vˆL€b‚€|é­ýêw!F&sparse_file€€¤u€^vˆÏ€oñu€^vˆÏ€oñu€^vˆÏ€oñ€_€x€tý7zXZi"Þ6!ÏXÌà]9¼CPv’Gô¨¨À*˜“X6ç^ÖØj}Ú„“3…£É¤h>0 ‹YZ­ýêw!R€rs6­ýêw!d€€€­ýêw!Eý7zXZi"Þ6!ÏXÌàiE]@0&ú+‡%r´’¡H™nË44iïzÄb¸Û¨·*Sգˉ֢-4éÊ™¥ÛMbaåt³`É+Á™ƒN¿gd ád9Ǻ]jtô‚B™ YZ­ýêw!r€ÏKR­ýêw!FBblock_device€€¼u€W|ý€fJu€W|ý€fJu€^vˆL€Râ€8Ä­ýêw!FPnamed_pipe€€¼u€W|ЀUñu€W|ЀUñu€^vˆL€‘b€/œ­ýêw!FDempty_subdir€€ýu€^vˆ€÷u€W}Å€€Šu€^vˆL€Âz€ˆ­ýêw!Fz€z­ýêw!Fz€z­ýêw!Ffplain text.txt€€¼u€^vˆÏ€oñu€^vˆÏ€oñu€^vˆÏ€oñ€w€3n€E/Hello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!Eý7zXZi"Þ6!ÏXÌàL]@0&ú+‡%r´’¡H™nË44i2ÿ¤=´Î1a öÁ¤2Ö.jaÝPI£[ZàsÒÒd0 ‹YZ­ýêw!r€0O­ýêw!FDdirectory_2€€íu€^vˆ€´u€W}Å€€Šu€^vˆL€¤¢z€W|­ýêw!FSlog€€¶u€Whg€þu€W ¡×€u€^vˆL€•€‹­ýêw!FCchar_device€€¤u€W}€‡—u€W}€‡—u€^vˆL€ÀB€š<­ýêw!Fmplain text 2.txt€X€ë]­ýêw!FLsymlink€€ÿu€W}¬€ ©1u€W}¬€‹u€^vˆL€ÀB€ŒJ­ýêw!FBblock_device€€¤u€W|ý€fJu€W|ý€fJu€^vˆL€°¢€„­ýêw!FPnamed_pipe€€¤u€W|ЀUñu€W|ЀUñu€^vˆL€•€3ò­ýêw!FDempty_subdir€€íu€^vˆ€Tu€W}Å€€Šu€^vˆL€…bz€}r­ýêw!Fz€z­ýêw!Fz€z­ýêw!Cý7zXZi"Þ6!ÏXÌàìÆ]|" ÅàËjëËݬY9ª¦ë’;º@7ýÒÍB\EÑ)xƒoYµ­Òå2RÈwüÓ3¶ÓÕi$®KfIU•Ýt nÓÍjg ŠDú Elò,ßC ³”|³¿ä Öþ‹8“-&'ðÈ© iOL{"®.¶LµÄP㵬!¥Ä×¶oÈC縊Pœ‰0ŒkÛºƒÕy €¿oäÌ—/‡á”!í ¥ãPŸ±òÖT4›G ëeT­àð^<þª¿Ô•¡taé²`5ìùä\ Ø%Mõܨƒ8R÷6ÄÅü9#±¡ÕÌuß§s~4ù…ð+kÆ€!Á±YFVçW)e Èš.ÐZg¯/JŒÙZ¾[vÇW¬^ýkiÑY°uY.𢤠E^¹°™ÁUྡ2»ÔMZ7B4 ˧ë€ùìã_Gý»“±†÷…Åä6²Ñ\Р¬ÚXü >¯€2£Þt«´:UmÁkrUÍKjIb¹Ø¯‹T¡I ùÆÏÉõ'Îà^yg *ñG=u‚dÎÅ,Qµt›Ž…öC5F¿ÙζÒ>T”`¦˜‰HŽÁÿ‘ŒcÉÆfãl›Ü¤1‚›ÿmûI©¾Í ‚(sÛ’&Þí 8q‡>0 ‹YZ€[À0:0xN/A€€Æ-€ hÀTdar-2.7.15/src/check/Old_format/archive_11-1.1.dar0000644000175000017500000001245414636066467016166 00000000000000{ˆàd cTT€€ ˆàd c0;1nN/A€@$­ýêw!Dˆàd c­ýêw!P.­ýêw!FDdirectory_1€€íu€^vˆ€È4u€W.€?u€^vˆL€¡z€,»­ýêw!FSlog€€¾u€Whg€þu€W ¡×€u€^vˆL€‘b€å­ýêw!FCchar_device€€¼u€W}€‡—u€W}€‡—u€^vˆL€b‚€Z†­ýêw!Fmplain text 2.txt€>fplain text 2.txt€€¼u€^vˆÏ€‘u€^vˆÏ€‘u€^vˆÏ€‘€w€9n€ý.Hello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!FLsymlink€€ÿu€W~ € u€W}¬€‹u€^vˆL€b‚€|é­ýêw!F&sparse_file€€¤u€^vˆÏ€oñu€^vˆÏ€oñu€^vˆÏ€oñ€_€n€brs6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF­ýêw!R€rs6­ýêw!d€€€­ýêw!E€system.nfs4_acl€P‡OWNER@GROUP@ EVERYONE@­ýêw!r€ÏKR­ýêw!FBblock_device€€¼u€W|ý€fJu€W|ý€fJu€^vˆL€Râ€8Ä­ýêw!FPnamed_pipe€€¼u€W|ЀUñu€W|ЀUñu€^vˆL€‘b€/œ­ýêw!FDempty_subdir€€ýu€^vˆ€÷u€W}Å€€Šu€^vˆL€Âz€ˆ­ýêw!Fz€z­ýêw!Fz€z­ýêw!Ffplain text.txt€€¼u€^vˆÏ€oñu€^vˆÏ€oñu€^vˆÏ€oñ€w€3n€D/Hello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!E€system.nfs4_acl€h OWNER@‡OWNER@§GROUP@ EVERYONE@­ýêw!r€0O­ýêw!FDdirectory_2€€íu€^vˆ€´u€W}Å€€Šu€^vˆL€¤¢z€W|­ýêw!FSlog€€¶u€Whg€þu€W ¡×€u€^vˆL€•€‹­ýêw!FCchar_device€€¤u€W}€‡—u€W}€‡—u€^vˆL€ÀB€š<­ýêw!Fmplain text 2.txt€X€ë]­ýêw!FLsymlink€€ÿu€W}¬€ ©1u€W}¬€‹u€^vˆL€ÀB€ŒJ­ýêw!FBblock_device€€¤u€W|ý€fJu€W|ý€fJu€^vˆL€°¢€„­ýêw!FPnamed_pipe€€¤u€W|ЀUñu€W|ЀUñu€^vˆL€•€3ò­ýêw!FDempty_subdir€€íu€^vˆ€Tu€W}Å€€Šu€^vˆL€…bz€}r­ýêw!Fz€z­ýêw!Fz€z­ýêw!Cˆàd c.droot€€s€u€^vˆL€°¢s€Ddirectory_1€€íu€^vˆ€È4u€W.€?u€^vˆL€¡Slog€€¾u€Whg€þu€W ¡×€u€^vˆL€‘bCchar_device€€¼u€W}€‡—u€W}€‡—u€^vˆL€b‚mplain text 2.txt€>fplain text 2.txt€€¼u€^vˆÏ€‘u€^vˆÏ€‘u€^vˆÏ€‘€w€¹€0O€9€k€9n€9}Lsymlink€€ÿu€W~ € u€W}¬€‹u€^vˆL€b‚&sparse_file€€¤u€^vˆÏ€oñu€^vˆÏ€oñu€^vˆÏ€oñ€_€ €ÏKR€€â€n€rs6€€€Bblock_device€€¼u€W|ý€fJu€W|ý€fJu€^vˆL€RâPnamed_pipe€€¼u€W|ЀUñu€W|ЀUñu€^vˆL€‘bDempty_subdir€€ýu€^vˆ€÷u€W}Å€€Šu€^vˆL€Âzzfplain text.txt€€¼u€^vˆÏ€oñu€^vˆÏ€oñu€^vˆÏ€oñ€w€ '€0O€3€ ߀3n€%;ihDdirectory_2€€íu€^vˆ€´u€W}Å€€Šu€^vˆL€¤¢Slog€€¶u€Whg€þu€W ¡×€u€^vˆL€•Cchar_device€€¤u€W}€‡—u€W}€‡—u€^vˆL€ÀBmplain text 2.txt€XLsymlink€€ÿu€W}¬€ ©1u€W}¬€‹u€^vˆL€ÀBBblock_device€€¤u€W|ý€fJu€W|ý€fJu€^vˆL€°¢Pnamed_pipe€€¤u€W|ЀUñu€W|ЀUñu€^vˆL€•Dempty_subdir€€íu€^vˆ€Tu€W}Å€€Šu€^vˆL€…bzzz€¾—r¾€îÀ0;1nN/A€€Ñ,€æÀTdar-2.7.15/src/check/Old_format/archive_10-1.1.dar0000644000175000017500000000405114636066467016157 00000000000000{K_†TT€€ K_†0:1xN/A€V%­ýêw!DK_†­ýêw!Fddirectory_1€€íu€_K&€g1u€W.€?u€_K€ ï €_z€_ ­ýêw!Eý7zXZi"Þ6!ÏXÌàiE]@0&ú+‡%r´’¡H™nË44iïzÄb¸Û¨Âè™´‘Þeb¡Ö)ò1àÂÇ´¸aSCŽÛ>,sî6Õw§³]jtô‚B™ YZ­ýêw!r€Ï+R­ýêw!Fmplain text 2.txt€>fplain text 2.txt€€¼u€^vˆÏ€‘u€^vˆÏ€‘u€_K€ þÀ€w€9n€”‹Hello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!Eý7zXZi"Þ6!ÏXÌàL]@0&ú+‡%r´’¡H™nË44i2ÿ¤=´Î1a öÁ¤2Ö.jaÝPI£[ZàsÒÒd0 ‹YZ­ýêw!r€0O­ýêw!Fdempty_subdir€€ýu€_K&€g1u€W}Å€€Šu€_K€ ï €_z€.­ýêw!Eý7zXZi"Þ6!ÏXÌàiF]@0&ú+‡%r´’¡H™nË44iïzÄb¸Û¨Âè™´‘Þeb¡Ö)ú5{:üÖ ‡¶Rº ˆ.| %øð^j·MÙ©B™ YZ­ýêw!r€ÏmR­ýêw!Fz€z­ýêw!Fz€z­ýêw!Ffplain text.txt€€¼u€^vˆÏ€oñu€^vˆÏ€oñu€_K€ þÀ€w€3n€=êHello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!Eý7zXZi"Þ6!ÏXÌàL]@0&ú+‡%r´’¡H™nË44i2ÿ¤=´Î1a öÁ¤2Ö.jaÝPI£[ZàsÒÒd0 ‹YZ­ýêw!r€0O­ýêw!Fddirectory_2€€íu€_K&€g1u€W}Å€€Šu€_K€ `€_z€Já­ýêw!Eý7zXZi"Þ6!ÏXÌàiE]@0&ú+‡%r´’¡H™nË44iïzÄb¸Û¨Âè™´‘Þeb¡Ö)ò1àÂÇ´¸aSCŽÛ>,sî6Õw§³]jtô‚B™ YZ­ýêw!r€Ï+R­ýêw!Fmplain text 2.txt€X€ë]­ýêw!Fdempty_subdir€€íu€_K&€vÑu€W}Å€€Šu€_K€ `€_z€à­ýêw!Eý7zXZi"Þ6!ÏXÌàiE]@0&ú+‡%r´’¡H™nË44iïzÄb¸Û¨Âè™´‘Þeb¡Ö)ò1àÂÇ´¸aSCŽÛ>,sî6Õw§³]jtô‚B™ YZ­ýêw!r€Ï+R­ýêw!Fz€z­ýêw!Fz€z­ýêw!Cý7zXZi"Þ6!ÏXÌàƒ]HÅeðÈšœ_*srï_9\"ÜDd¯5êÏÅô+VNìÃÇ4ÖR‡Eùdðâ†&FÚvù˦\u ”œ˜ý8ègqã˜A•A)a“åâ¢xi›&~Ï`»Aú”ÐPD–*hÏ“byeäŽïè$ö üÒÙ_{ÃÑzâçZeáÚÕåþ1KÙEôgïð±‡Îæ^#8´‚ ;™F®F˜8¤¬ÆŸe+¬¥&F5]5¯¯¤aª Êäýÿ!¿ÚÑ-ÛÊÝÉY klñ¹b8{GhkF;£;ôWÈþÎÐ ½ÕzUÿ’@s¾I”+5PɳM|zmšãW T|.ŒI•6‚ßEº$¼˜„ðP,>0 ‹YZ€žÀ0:1xN/A€€Ç-€ãÀTdar-2.7.15/src/check/Old_format/archive_11-2.1.dar0000644000175000017500000000354214636066467016165 00000000000000{5e~yTT€€ 5e~y0;2zN/A€W$­ýêw!D5e~y­ýêw!P/mnt/Externe/Espace/Denis/DAR-Working-Rep-Stable/dar/src/check/Old_format/A­ýêw!Fddirectory_2€€íu€^vˆ€´u€W}Å€€Šn€e5€¥&ì€_z€sl­ýêw!ExÚk````,®,.IÍÕËK+6‰OLÎah 13 ˆ1>’lþá~®A  İ$îä€)Îéæéïç ’Éð­ýêw!r€Ï+R­ýêw!Fmplain text 2.txt€>fplain text 2.txt€€¼u€^vˆÏ€‘u€^vˆÏ€‘n€e5€¥&ì€w€9n€eHello Word! This is a plain file with several hard links ­ýêw!R€9}­ýêw!ExÚk````,®,.IÍÕËK+6‰OLÎah f1 H’€˜Í?ÜÏ5ÈÄÛ±Š 1,‰»ù‡ Š7IN×0× H?Wo­ýêw!r€0O­ýêw!Fdempty_subdir€€íu€^vˆ€Tu€W}Å€€Šn€e5€¥&ì€_z€l¶­ýêw!ExÚk````,®,.IÍÕËK+6‰OLÎah 13 ˆ1>’lþá~®A  İ$îä€)Îéæéïç ’Éð­ýêw!r€Ï+R­ýêw!Fz€z­ýêw!Fz€z­ýêw!Fddirectory_1€€íu€^vˆ€È4u€W.€?n€e5€h¶€_z€1•­ýêw!ExÚk````,®,.IÍÕËK+6‰OLÎah 13 ˆ1>’lþá~®A  İ$îä€)Îéæéïç ’Éð­ýêw!r€Ï+R­ýêw!Fmplain text 2.txt€X€ë]­ýêw!Fdempty_subdir€€ýu€^vˆ€÷u€W}Å€€Šn€e5€h¶€_z€¦`­ýêw!ExÚk````,®,.IÍÕËK+6‰OLÎah 13 ˆ1>’lþá~®A  Äwò @_$9]Ã\ƒ"ýý\ARÔS6­ýêw!r€ÏmR­ýêw!Fz€z­ýêw!Fz€z­ýêw!Ffplain text.txt€€¼u€^vˆÏ€oñu€^vˆÏ€oñn€e5€¥&ì€w€3n€»Hello Word! This is a plain file without hard link ­ýêw!R€%;ih­ýêw!ExÚk````,®,.IÍÕËK+6‰OLÎah f1 H’€˜Í?ÜÏ5ÈÄÛ±Š 1,‰»ù‡ Š7IN×0× H?Wo­ýêw!r€0O­ýêw!CxÚ“5•Le‚ºJýܼ}׊’Ô¢¼T}×â‚ÄäT}—Ô¼Ìb}Ç Ýðü¢ì̼tÝ ÔÝà’ĤœTý”Ä"ýâ¢dýäŒÔäl}ÿœ”ø´ü¢ÜÄ}G†”¢üüæÉ`‚¡Lç5¤Jšr4È/U{HIÉ,JM.É/ªŒ7b`Dh`|[ÚWÖÑØÀÀ̶¥´!\¾öhSC’ uñ â8ˆ`9¯ÄŸ[“˜™§P’ZQ¢`¤WRQ1Ì. CÙª=`«Î70ðÕODb¢YU$€­j4ð—1,AB“À, Ï@2,–µ)©¹%•ñÅ¥I@¿áð“X~?1Mƒû©ª )Œ ±›ÇtÂd^½^‡}/̼ Ùm0ó˜…Qngÿ…[ó]‹³‘¬a1X“ r6R ã òüHL,AÎê…äÆ !&0 äªÖ™U`ÖI¦©ý"èΧ€±À0;2zN/A€€Æ,€ÀTdar-2.7.15/src/check/Makefile.in0000644000175000017500000006317614640025155013225 00000000000000# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### # # conditionnal part: --enable-mode=... # VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = all_features$(EXEEXT) padder$(EXEEXT) \ bnonzero$(EXEEXT) subdir = src/check ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(dist_noinst_DATA) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = PROGRAMS = $(noinst_PROGRAMS) am_all_features_OBJECTS = all_features.$(OBJEXT) all_features_OBJECTS = $(am_all_features_OBJECTS) all_features_LDADD = $(LDADD) am__DEPENDENCIES_1 = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_bnonzero_OBJECTS = bnonzero.$(OBJEXT) bnonzero_OBJECTS = $(am_bnonzero_OBJECTS) bnonzero_LDADD = $(LDADD) bnonzero_DEPENDENCIES = ../libdar/$(MYLIB).la $(am__DEPENDENCIES_1) am_padder_OBJECTS = padder.$(OBJEXT) padder_OBJECTS = $(am_padder_OBJECTS) padder_LDADD = $(LDADD) padder_DEPENDENCIES = ../libdar/$(MYLIB).la $(am__DEPENDENCIES_1) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/all_features.Po \ ./$(DEPDIR)/bnonzero.Po ./$(DEPDIR)/padder.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(all_features_SOURCES) $(bnonzero_SOURCES) \ $(padder_SOURCES) DIST_SOURCES = $(all_features_SOURCES) $(bnonzero_SOURCES) \ $(padder_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac DATA = $(dist_noinst_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CXXSTDFLAGS = @CXXSTDFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN_PROG = @DOXYGEN_PROG@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GPGME_CFLAGS = @GPGME_CFLAGS@ GPGME_CONFIG = @GPGME_CONFIG@ GPGME_LIBS = @GPGME_LIBS@ GPGRT_CONFIG = @GPGRT_CONFIG@ GREP = @GREP@ HAS_DOT = @HAS_DOT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBCURL_CFLAGS = @LIBCURL_CFLAGS@ LIBCURL_LIBS = @LIBCURL_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTHREADAR_CFLAGS = @LIBTHREADAR_CFLAGS@ LIBTHREADAR_LIBS = @LIBTHREADAR_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGMERGE = @MSGMERGE@ MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POSUB = @POSUB@ PYEXT = @PYEXT@ PYFLAGS = @PYFLAGS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ UPX_PROG = @UPX_PROG@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dot = @dot@ doxygen = @doxygen@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ groff = @groff@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ tmp = @tmp@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ upx = @upx@ @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@MYLIB = libdar @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@MYLIB = libdar64 @BUILD_MODE32_TRUE@MYLIB = libdar32 @BUILD_MODE32_FALSE@@BUILD_MODE64_FALSE@AM_CPPFLAGS = -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) @BUILD_MODE32_FALSE@@BUILD_MODE64_TRUE@AM_CPPFLAGS = -DLIBDAR_MODE=64 -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) @BUILD_MODE32_TRUE@AM_CPPFLAGS = -DLIBDAR_MODE=32 -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) helpers = build_tree.sh final.sh main.sh modif_tree.sh routine.sh loop.sh make_env.sh dist_noinst_DATA = $(helpers) Old_format/archive_01.1.dar Old_format/archive_02.1.dar Old_format/archive_03.1.dar Old_format/archive_03_nozip.1.dar Old_format/archive_04.1.dar Old_format/archive_05.1.dar Old_format/archive_06.1.dar Old_format/archive_07.1.dar Old_format/archive_08.1.dar Old_format/archive_08-1.1.dar Old_format/archive_08-1_crypto_bf_test.1.dar Old_format/archive_08-1_nozip.1.dar Old_format/archive_09.1.dar Old_format/archive_09_crypto_bf_test.1.dar Old_format/archive_09_nozip.1.dar Old_format/archive_10.1.dar Old_format/archive_10-1.1.dar Old_format/archive_10-1_nozip.1.dar Old_format/archive_10-1_crypto_bf_test.1.dar Old_format/archive_11-1.1.dar Old_format/archive_11-2.1.dar sftp_mdelete ftp_mdelete LDADD = ../libdar/$(MYLIB).la $(LTLIBINTL) AM_LDFLAGS = MY_MAKEFILE = my_makefile MY_ENV = my_env all_features_SOURCES = all_features.cpp all_features_DEPENDENCIES = ../libdar/$(MYLIB).la padder_SOURCES = padder.cpp bnonzero_SOURCES = bnonzero.c all: all-am .SUFFIXES: .SUFFIXES: .c .cpp .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/check/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/check/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list all_features$(EXEEXT): $(all_features_OBJECTS) $(all_features_DEPENDENCIES) $(EXTRA_all_features_DEPENDENCIES) @rm -f all_features$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(all_features_OBJECTS) $(all_features_LDADD) $(LIBS) bnonzero$(EXEEXT): $(bnonzero_OBJECTS) $(bnonzero_DEPENDENCIES) $(EXTRA_bnonzero_DEPENDENCIES) @rm -f bnonzero$(EXEEXT) $(AM_V_CCLD)$(LINK) $(bnonzero_OBJECTS) $(bnonzero_LDADD) $(LIBS) padder$(EXEEXT): $(padder_OBJECTS) $(padder_DEPENDENCIES) $(EXTRA_padder_DEPENDENCIES) @rm -f padder$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(padder_OBJECTS) $(padder_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/all_features.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bnonzero.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/padder.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: check-am all-am: Makefile $(PROGRAMS) $(DATA) all-local installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-local clean-noinstPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/all_features.Po -rm -f ./$(DEPDIR)/bnonzero.Po -rm -f ./$(DEPDIR)/padder.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/all_features.Po -rm -f ./$(DEPDIR)/bnonzero.Po -rm -f ./$(DEPDIR)/padder.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ check-am check-local clean clean-generic clean-libtool \ clean-local clean-noinstPROGRAMS cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile $(MY_ENV): ./make_env.sh @ chmod a+x $(helpers) @ ./make_env.sh $(MY_ENV) $(MY_MAKEFILE): $(MY_ENV) ./loop.sh @ ./loop.sh check-local: $(MY_MAKEFILE) $(MAKE) -f $(MY_MAKEFILE) all @ ./final.sh clean-local: rm -rf target_* rm -f $(MY_MAKEFILE) $(MY_ENV) rm -f compressible_file uncompressible_file all-local: compressible_file uncompressible_file compressible_file: bnonzero rm -f compressible_file ./bnonzero compressible_file 1 2048 uncompressible_file: bzip2 -c9 < `which sh` > uncompressible_file # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dar-2.7.15/src/check/Makefile.am0000644000175000017500000000612614636066467013224 00000000000000####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### # # conditionnal part: --enable-mode=... # if BUILD_MODE32 MYLIB=libdar32 AM_CPPFLAGS=-DLIBDAR_MODE=32 -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) else if BUILD_MODE64 MYLIB=libdar64 AM_CPPFLAGS=-DLIBDAR_MODE=64 -I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) else MYLIB=libdar AM_CPPFLAGS=-I'$(srcdir)/../libdar' -DDAR_LOCALEDIR=\"$(localedir)\" -DDAR_SYS_DIR=\"$(system_path)\" $(CPP_PROF) endif endif helpers = build_tree.sh final.sh main.sh modif_tree.sh routine.sh loop.sh make_env.sh dist_noinst_DATA = $(helpers) Old_format/archive_01.1.dar Old_format/archive_02.1.dar Old_format/archive_03.1.dar Old_format/archive_03_nozip.1.dar Old_format/archive_04.1.dar Old_format/archive_05.1.dar Old_format/archive_06.1.dar Old_format/archive_07.1.dar Old_format/archive_08.1.dar Old_format/archive_08-1.1.dar Old_format/archive_08-1_crypto_bf_test.1.dar Old_format/archive_08-1_nozip.1.dar Old_format/archive_09.1.dar Old_format/archive_09_crypto_bf_test.1.dar Old_format/archive_09_nozip.1.dar Old_format/archive_10.1.dar Old_format/archive_10-1.1.dar Old_format/archive_10-1_nozip.1.dar Old_format/archive_10-1_crypto_bf_test.1.dar Old_format/archive_11-1.1.dar Old_format/archive_11-2.1.dar sftp_mdelete ftp_mdelete noinst_PROGRAMS = all_features padder bnonzero LDADD = ../libdar/$(MYLIB).la $(LTLIBINTL) AM_LDFLAGS = MY_MAKEFILE=my_makefile MY_ENV=my_env $(MY_ENV): ./make_env.sh @ chmod a+x $(helpers) @ ./make_env.sh $(MY_ENV) $(MY_MAKEFILE): $(MY_ENV) ./loop.sh @ ./loop.sh check-local: $(MY_MAKEFILE) $(MAKE) -f $(MY_MAKEFILE) all @ ./final.sh all_features_SOURCES = all_features.cpp all_features_DEPENDENCIES = ../libdar/$(MYLIB).la padder_SOURCES = padder.cpp bnonzero_SOURCES = bnonzero.c clean-local: rm -rf target_* rm -f $(MY_MAKEFILE) $(MY_ENV) rm -f compressible_file uncompressible_file all-local: compressible_file uncompressible_file compressible_file: bnonzero rm -f compressible_file ./bnonzero compressible_file 1 2048 uncompressible_file: bzip2 -c9 < `which sh` > uncompressible_file dar-2.7.15/src/check/ftp_mdelete0000755000175000017500000000262114636066467013402 00000000000000#!/bin/bash ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### if [ -z "$1" -o -z "$2" ] ; then echo "usage: $0 " echo "will remove pattern in the directory pointed to by the URL" exit 1 fi script="_$2" echo "script = $script" echo "$1" | sed -r -n -e 's#^ftp://([^:@/]+):([^:@/]+)@([^:@/]+)(.*)$#ftp -n< "$script" echo "mdelete $2" >> "$script" echo "EOF" >> "$script" source "$script" rm "$script" dar-2.7.15/src/check/build_tree.sh0000755000175000017500000000514114636066467013641 00000000000000#!/bin/sh ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### if [ $# -ne 2 ] ; then echo "usage: $0 " exit 1 fi DIRNAME=`pwd`/`dirname $0` COMP_FILE="$DIRNAME/compressible_file" UNCOMP_FILE="$DIRNAME/uncompressible_file" if [ ! -f $COMP_FILE ] ; then echo "missing file: $COMP_FILE" exit 2 fi SUB1=S"$2"B1 SUB2=S"$2"B2 SUB3=S"$2"B3 mkdir "$1" cd "$1" echo "this is the content of a plain file" > plain_file.txt if [ -x `which chattr` ] ; then chattr +cdS plain_file.txt fi setfattr -n user.coucou -v hello plain_file.txt mkdir "$SUB1" setfattr -n user.cuicui -v hellu "$SUB1" cd "$SUB1" ln ../plain_file.txt hard_linked_inode.txt cd .. mkdir "$SUB2" cd "$SUB2" ln -s "../$SUB1"/hard_linked_inode.txt symlink.txt ln symlink.txt hard_to_symlink.txt dd bs=4096 seek=10 count=1 if=/dev/zero of=sparse.txt 1> /dev/null 2> /dev/null echo "some chars in the middle of holes" >> sparse.txt dd bs=4096 conv=notrunc if=/dev/zero count=10 >> sparse.txt 2> /dev/null cp sparse.txt sparse2.txt mkfifo tube1 cd .. mkdir "$SUB3" cd "$SUB3" ln ../plain_file.txt mkdir T cd T touch "another plain file" > titi.txt cd .. cd .. ln "$SUB2/sparse2.txt" "hard_linked_sparse.txt" mkfifo tube2 cd "$SUB1" ln ../tube2 hard_linked_pipe mknod chardev c 1 1 setfacl -m u:bin:rw- chardev mknod blockdev b 1 1 ln chardev chardev_hard ln blockdev blockdev_hard echo "another simple plain file" > plain_file2.txt chgrp root plain_file2.txt # need a bigger file to test compression, and a easy one but non-sparse to compress cp "$COMP_FILE" . # need an uncompressible file to test skip back upon poor compression cp "$UNCOMP_FILE" . # file used to test binary delta cp "$UNCOMP_FILE" for_binary_delta dar-2.7.15/src/check/make_env.sh0000755000175000017500000000735614636066467013322 00000000000000#!/bin/sh ####################################################################### # dar - disk archive - a backup/restoration program # Copyright (C) 2002-2024 Denis Corbin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # to contact the author, see the AUTHOR file ####################################################################### if [ "$1" = "" ] ; then echo "usage $0: " exit 1 fi CWD=`pwd` if [ ! -x ../dar_suite/dar ] ; then echo "********************************************" echo "" echo "Dar is not built, aborting" echo "" echo "********************************************" exit 2 else export DAR=$CWD/../dar_suite/dar fi if [ ! -x ../dar_suite/dar_slave ] ; then echo "********************************************" echo "" echo "Dar_slave is not built, aborting" echo "" echo "********************************************" exit 2 else export DAR_SLAVE=$CWD/../dar_suite/dar_slave fi if [ ! -x ../dar_suite/dar_xform ] ; then echo "********************************************" echo "" echo "Dar_xform is not built, aborting" echo "" echo "********************************************" exit 2 else export DAR_XFORM=$CWD/../dar_suite/dar_xform fi if [ ! -x ./all_features ] ; then echo "********************************************" echo "" echo "all_features program not built, aborting" echo "" echo "********************************************" exit 2 fi if [ `id -u` -ne 0 ]; then echo "********************************************" echo "" echo "need to be run as root" echo "" echo "********************************************" exit 3 fi if [ -z "$DAR_KEY" ] ; then echo "You need to set the environmental variable" echo "DAR_KEY to an email for which you have public" echo "and private key available for encryption and" echo "signature." echo "You can use the GNUPGHOME variable to" echo "point to another keyring than ~root/.gnupg" echo "" echo "Example of use with bash:" echo "export DAR_KEY=your.email@your.domain" echo "export GNUPGHOME=~me/.gnupg" echo "" echo "Example of use with tcsh:" echo "setenv DAR_KEY your.email@your.domain" echo "setenv GNUPGHOME ~me/.gnupg" exit 3 fi if [ -z "$DAR_SFTP_REPO" -o -z "$DAR_FTP_REPO" ] ; then echo "You need to set the environment variables" echo "DAR_SFTP_REPO and DAR_FTP_REPO with an URL where" echo "to upload and download file using SFTP and FTP" echo "respectively, for example sftp://login:pass@host/some/path" echo "and ftp://login:pass@host/some/path respectively" echo "" echo "Dar will need a properly set known_hosts file, you" echo "can make dar using a different file than ~/.ssh/known_hosts" echo "by setting the environment variable DAR_SFTP_KNOWNHOSTS_FILE" echo "" exit 3 fi if ./all_features ; then echo "OK, all required features are available for testing" else exit 3 fi echo "Generating file $1" cat > "$1" <" echo "example: $0 A1 B1 B2 B3" exit 1 fi ## environement required: # OPT environment variable (DCF file for common parameters) if [ "$OPT" = "" ]; then echo '$OPT not set' exit 2 fi if [ -z "$OPT_NOSEQ" ] ; then echo '$OPT_NOSEQ not set' exit 2 fi if [ "$DAR" = "" ]; then echo '$DAR not set' exit 2 fi if [ ! -z "$ROUTINE_DEBUG" -a "$ROUTINE_DEBUG" != "debug" ] ; then echo 'unvalid value given to environment variable ROUTINE_DEBUG' exit 2 fi # DAR environment variable points to dar # ## relies on: # ./build_tree.sh # ./modif_tree.sh src=SRC dst=DST src2=SRC2 dst2=DST2 full=full catf=catf catf_fly=catf_fly full_delta=full_delta cat_full_delta=cat_full_delta catf_delta=catf_delta double_catf=double_catf double_catf_fly=double_catf_fly diff=diff diff_delta=diff_delta diff_delta2=diff_delta2 catd=catd catd_fly=catd_fly double_catd=double_catd double_catd_fly=double_catd_fly diff_fly=diff_fly diff_double=diff_double diff_double_fly=diff_double_fly merge_full=merge_full merge_diff=merge_diff merge_delta=merge_delta full2=full2 diff2=diff2 full3=full3 decr=decr todar=todar toslave=toslave piped=piped piped_fly=piped_fly xformed1=xformed1 xformed2=xformed2 xformed3=xformed3 if [ "$1" != "NONE" ] ; then hash="`sed -r -n -e 's/--hash (.*)/\1/p' tmp.file | tail -n 1`" fi hash_xform="-3 $hash" if [ "$hash" = "" ] ; then hash="none" hash_xform="" fi function my_diff { if [ "$1" = "" -o "$2" = "" ] ; then echo "usage: $0 " return 1 fi cd "$1" tar -cf "../$1.tmp.tar" . cd "../$2" tar -df "../$1.tmp.tar" local ret1=$? tar -cf "../$2.tmp.tar" . cd "../$1" tar -df "../$2.tmp.tar" local ret2=$? cd .. rm "$1.tmp.tar" "$2.tmp.tar" return `[ $ret1 -eq 0 -a $ret2 -eq 0 ]` } function GO { if [ "$1" == "" ] ; then echo "usage: $0