LIBDAR
APPLICATION INTERFACE
TUTORIAL
for API version 3.0.x
Presentation
Libdar is a library that has been
built from source code
initially located in dar command
line application. The
features that libdar provides are the ability to handle
Disk ARchive (dar)'s archives,
thanks to the following operations:
- archive creation,
- file extraction,
- archive listing,
- archive testing,
- archive comparison,
- catalogue isolation.
Note that Disk ARchive has been released
under the Gnu General Public License
(GPL), and so is the code of libdar. So, to link your code with
libdar (statically or dynamically), your
application must also be covered by the GPL.
This tutorial will show you how to
use libdar from
the API. As the dar command
line now also uses this API, looking at his code may also be
interesting as illustration, in particular the file dar_suite/dar.cpp.
The sample codes provided here are
very simple code that could let you
see overall process of using libdar, they may or may not compile, I
have even not tried to, so just use them as illustration, no more. Note
that there is a documentation
reference for this API which is built with dar under the directory doc/html if you have Doxygen available and can be consulted on the
web.
Let's Start
conventions
Language
Dar and libdar are written in C++, and so is the libdar API. Libdar is thus easily usable with C or C++ code. For other languages, it depends on so many things (calling convention, etc.) that I would just say you are welcome to provide the necessary adaptation layer for any particular language. :-)
Libdar namespace
All libdar symbols are defined under
the libdar namespace. You
can either add the using namespace
libdar; line at the beginning of your source files:
using
namespace libdar;
get_version(....);
|
or, as we
will do in the following examples, you can explicitely use the
namespace in front of libdar objects :
libdar::get_version(....);
|
Exceptions or not Exceptions
For each point we will see two ways
of using libdar.
One, is using exception, the other is without exceptions.
They will be displayed this way :
example
code using exceptions
|
example
code not using exceptions
|
All exceptions used by libdar
inherit from the pure virtual class Egeneric. The only
method you
will need to know about for any exception is the get_message()
call,
which returns a string explaining the reason of the
message in a human language. The type of the error is defined by the
class of the
exception. Here follows all the exception classes and their meaning:
class
libdar::Egeneric
|
this is the parent class of all
exception classes (a pure virtual class)
|
class libdar::Ememory
|
exception used when memory has
been exhausted
|
class libdar::Ebug
|
exception used to signal a bug.
A bug is triggered when reaching some code that should never be reached
|
class libdar::Einfinint
|
exception used when arithmetic
error is detected when operating on infinint
|
class libdar::Elimitint
|
exception used when a limitint
overflow is detected, the maximum value of the limitint has been
exceeded
|
class libdar::Erange
|
exception used to signal range
error
|
class libdar::Edeci
|
exception used to signal
convertion problem between infinint and string (decimal representation)
|
class libdar::Efeature
|
exception used when a requested
feature is not (yet) implemented
|
class libdar::Ehardware
|
exception used when hardware
problem is found
|
class libdar::Euser_abort
|
exception used to signal that
the user has aborted the operation
|
class libdar::Edata
|
exception used when an error
concerning the treated data has been met
|
class libdar::Escript
|
exception used when the
inter-slice user command returned an error code
|
class libdar::Elibcall
|
exception used to signal an
error in the arguments given to a libdar call of the API
|
class libdar::Ecompilation
|
exception used when a requested
fearture has not beed activated at compilation time
|
1 - First we *must* check the libdar version
// we include this to be able to display some
// messages
#include <io.h>
// we include this header to access lidbar API
#include <dar/libdar.h>
// all sample code shown will be inside this
// function for simplicity of the examples
void my_sample_function()
{
try
{
libdar::U_I maj, med, min;
// first we MUST call
get_version()
libdar::get_version(maj,
med, min);
if(maj !=
libdar::LIBDAR_COMPILE_TIME_MAJOR ||
med <
libdar::LIBDAR_COMPILE_TIME_MEDIUM)
throw libdar::Erange("initialization",
"we are linking against a
wrong libdar");
}
catch(libdar::Egeneric & e)
{
std::cout << e.get_message()
<<
std::endl;
}
}
|
// we include this to be able to display some
// messages
#include <io.h>
// we include this header to access lidbar API
#include <dar/libdar.h>
// all sample code shown will be inside this
// function for simplicity of the examples
void my_sample_function()
{
libdar::U_I maj, med, min;
libdar::U_16 excode;
std::string msg;
// first we MUST call
get_version()
libdar::get_version_noexcept(maj,
med, min,
excode, msg);
if(excode != LIBDAR_NOEXCEPT)
{
std::cout << msg << endl;
return;
}
if(maj != LIBDAR_COMPILE_TIME_MAJOR ||
med <
libdar::LIBDAR_COMPILE_TIME_MEDIUM )
{
std::cout << "we are
linking against wrong libdar" << std::endl;
return;
}
|
The get_version* family function must be called for
several reasons :
-
you must check that the library
you dynamically link with is compatible with the features you will use.
The major number must be the same as no compatibility is assured
between two libdar versions of different major number. And, the medium
number must be greater or equal to the one used at compilation time to
be sure
that all the feature you have been using are available in the libdar
library you dynamically link with. Changes between minor versions
correspond to bug
fixes which does not implies any API change, so there is no constraint
about minor number.
- the get_version* calls, over
returning version information, makes important initialization tasks for
libdar. If it is not called first, the libdar library is not
initialized and its behavior is unpredictable. Note that you may call
get_version* several time if you wish, and in any flavor.
2 - Let's see the compilation time features
once we have called a function of the get_version*
family it is possible to access the features activated at compilation
time:
void my_sample_function()
{
// let's continue in the
same function
bool ea;
bool largefile;
bool nodump;
bool special_alloc;
libdar::U_I bits;
bool thread_safe;
bool libz;
bool libbz2;
bool libcrypto;
libdar::get_compile_time_features(ea,
largefile,
nodump,
special_alloc,
bits,
thread_safe,
libz,
libbz2,
libcrypto);
}
|
// here there is no difference because no exceptions
// are thrown by the get_compile_time_feature()
// function
|
You can do what you want with the
resulting values, like displaying the libdar available feature, or even
filter if you don't want to continue when a particular feature is
missing. But, that's maybe not necessary to worry here, because libdar
will tell you if the operation you ask requires a feature that has not
been activated at compilation time thanks to the Ecompile exception or the LIBDAR_ECOMPILATION error code.
3 -User interaction
The generic user_interaction
class
To be able to report messages to the user and to be able to ask
questions
to the user, a special class called
user_interaction has
been introduced as first argument of the
API calls we will see below. Rawly, user_interaction
is a pure virtual class from which you can define your own inherited
class to have your very own implementation of the interaction with the
user (GUI's graphical interaction, for example). You will just have to
override four methods which prototypes follow :
void pause (const std::string
&message);
this method is
called by libdar to request an boolean answer from the user. The
question is given in argument and the pause()
method must either return
if the user agreed the question (said
"yes" or "true"), or throw a Euser_abort
exception if the user refused the proposition. Don't
worry about thowing exception in your code, it will be trapped by
libdar if you don't want to manage exceptions, and use libdar in the
"no exception" manner.
void warning (const std::string
&message);
libdar call this
method to display an informational message to the user. It may not
always be a warning as the name suggests, sometimes it is just
normal information.
std::string get_string (const std::string
&message, bool echo);
This call is used
to get a arbitrary answer from the user. As this is actually mainly
used to get a password from the user (when no password has been suplied
for an encrypted archive), the echo argument
tells that the user answer shall or shall not be displayed on screen.
If echo is set to "false" the
implementation of get_string() must
hide the
character typed by the user in answer to the displayed question.
user_interaction * clone
() const;
This is a
technical call that must return a copy of the "this" object, like would
do the copy constructor. Why not simply use the copy constructor?
because libdar may need to keep an internal copy of the object. But the
object which class is an inherited class of
user_interaction is not know by
his real class but through a
user_interaction
pointer type. So invoking the copy constructor of the
user_interaction
class will make the new object copied only for the
user_interaction part, this may
cause a crash when using the object copy. A simple implementation of
this method should be something like this (even if you don't want to
use exceptions):
user_interaction
*my_own_class::clone() const
{
my_own_class *ret = new my_own_class(*this);
if(ret == NULL)
throw
Ememory("user_interaction_callback::clone");
else
return ret;
}
|
The callback interaction class
A inherited
class from
user_interaction
called
user_interaction_callback
proposes an implementation of the user interaction based on callback
functions. This just replaces the three interactions methods (pause,
warning and get_string) by three normal functions of your choice, which
must be given to the
user_interaction_callback's
constructor and you don't have to consider the
clone() method. But you still need
to implement theses callback functions. For a real
example, I propose you to have a look at what is done for
dar's command line.
dar's user interaction is done
thanks to an object of the
user_interaction_callback
class and three static functions of the module
dar_suite/shell_interaction.cpp
Just a note however about the
contextual
value present in the arguments of theses callback functions :
// our own callback functions.
// for the illustration of what theses 'context' arguments
// can be used for we will imagine the situation where
// multiple windows or multiple threads may each one use
// libdar. But all share the same callback functions
typedef class t_window_type t_win;
// this is an arbitrary type that here we will say
// points to a graphical window object wrapped in a C++
// class.
// Note that the show() wait_for_click() and so one
// methods attributed to the t_win class are absolutely
// imaginary. Any link to an existing class is a pure
// coincidence...
void warning_callback(const std::string &x, void *context)
{
(t_win *)(context)->show(x);
}
bool answer_callback(const std::string &x, void *context)
{
click_type ret;
(t_win *)(context)->show(x);
ret = (t_win *)(context)->wait_for_click();
return ret == click_OK;
}
std::string string_callback(const std::string &x, bool echo, void
*context)
{
(t_win *)(context)->show(x);
if(!echo)
(t_win *)(context)->set_hide_typed_char();
(t_win *)(context)->wait_for_click();
return (t_win *)(context)->read_text();
}
---------8<-------8<-------8<-------
// So now each window can have it user_interaction object based
on the same
libdar::user_interaction_callback dialog =
libdar::user_interaction_callback(&warning_callback,
&answer_callback, &string_callback,
(void *)get_current_windows_id());
// just the "context" argument changes, and will be passed asis
from the constructor to the callback
// functions
|
4 - Masks
Mask are used to define which file
are to be considered and which are
not to be considered. Libdar implements masks as several classes that
all inherit from a pure virtual class that defines the way masks are
used. This root class is the class
mask and provides the is_covered()
method for libdar to know which files to consider. Here follows the
different basic masks classes you
can use to build very complex masks:
class libdar::mask
|
the generic class, parent of all
masks (a pure virtual class)
|
class libdar::bool_mask
|
boolean mask, either always true
or false, it matches either all files or no files at all
|
class libdar::simple_mask
|
matches as done by the shell on
the command
lines (see "man 7 glob")
|
class libdar::regular_mask
|
matches regular expressions (see
"man 7 regex")
|
class libdar::not_mask
|
negation of another mask
|
class libdar::et_mask
|
makes an *AND* operator between
two or more masks
|
class libdar::ou_mask
|
makes the *OR* operator
between two or more masks
|
class lbdar::simple_path_mask
|
string matches if it is subdir
of mask or mask is a subdir of expression
|
class libdar::same_path_mask
|
matches if the string is exactly
the
given mask (no wilde card expression)
|
class
libdar::exclude_dir_mask
|
matches if string is the given
string or a sub directory of it
|
Let's play with some masks :
// all files will be
elected by this mask
libdar::bool_mask m1 = true;
// all file that match the glob
expession "A*~" will match.
// the second argument of the
constructor tell if the match is case sensitive so here
// any file beginning by 'A' or by 'a'
and ending by '~' will be elected by this mask
libdar::simple_mask m2 = libdar::simple_mask(std::string("A*~"),
false);
// m3 is the negation if m2. This mask
will thus match
// any file that does not begin by 'A'
or 'a' and also finish by '~'
libdar::not_mask m3 = m2;
// this mask matches any file that is a
subdirectory of "/home/joe"
// and any file that has /home/joe as
subdirectory, that is:
// "/", "/home", "/jome/joe" and any
subdirectory are matched.
// the second argument here too is the
case sensitivity (so here
// "/HoMe" will not be elected by
this mask.
libdar::simple_path_mask m4 = simple_path_mask("/home/joe",
true);
// now let's do some more complex things:
// m5 will now match
only files that are elected by both m2 AND m4
libdar::et_mask m5;
m5.add_mask(m2);
m5.add_mask(m4);
// we can make more silly things like
this, where m5 will select files
// that match m2 AND m4 AND m3. But m3 =
not m2 so now m5 will never
// match any file...
m5.add_mask(m3);
// but we could do the same with a
"ou_mask" that realizes the OR operation
// and we would get an silly equivalent
of m1 (a mask that matches any files)
libdar::ou_mask m6;
m6.add_mask(m2);
m6.add_mask(m4);
m6.add_mask(m3);
// last point, the NOT, AND and OR
operation can be used recurively :
// even this, is
possible where two ask are used as reference for each other !
libdar::not_mask m7 = m6;
m6.add_mask(m7);
|
Now that you see a bit more all the power of masks, you must know that
in
quite all libdar operations two masks are required:
- The first mask is used against
filenames, but is not applied to directories, nor it is applied to the
path part of the filenames. This first mask may be any
combinaison of the masks seen previously, it will only be applied to
socket, named pipes, symbolic links, char or block devices, plain
files, but not to directories as we said. This way you can filter by
type of file
you want or don't want to save, restore, list, compaire, compress and
so on.
- The second mask is applied to any
file including directories and including the path part of the filename.
So with it you can prune directories, or in
the other way restrict the operation to a particula subdirectory, as
well as to a particular plain file for example.
5 - Let's create a simple archive
Now that we have seen masks and exceptions classes let's start the real
thing:
// creating an archive is simple, it is just
// calling the "create" constructor of the archive
// class. It may be used for full or differential
// archive. But we will see differential later.
// my_arch should have been an object instead of a
// pointer to an archive object.
libdar::user_interaction_callback dialog =
libdar::user_interaction_callback(ptr1, ptr2, ptr3);
// where ptr1, ptr2 and ptr3 are three callback
// functions.
libdar::statistics ret;
// we will see this structure a bit further
libdar::archive *my_arch =
new libdar::archive(dialog,
"/home", // saving all under this "root"
"/tmp", // where the slices will go
NULL, // we do a full
backup for now
libdar::not_mask(simple_mask("*~", true)),
//
we don't save the files ending with
// '~' this does not concern
directories
libdar::bool_mask(true),
//
all directories and files not rejected
//
by the previous mask are saved
"my_archive",
//
the basename of the slices
"dar", // dar's slice extensions
true, // we allow slice overwriting
true, // but ask to be warned in such
occurence
false, // we don't want a verbose output
false, // nor we want to pause between slices
true, // rejected directory will be
saved as
// empty (no directory is
rejected so
// here there is no
importance for that
// parameter)
libdar::gzip // the archive will be compressed
// using gzip
9, // at maximum
compression level
0, // no slicing is
done,
0, // so the first slice
size must also be
//
set to zero
true, // root EA will be saved if present
true, // user EA too
"", // no script will be
executed between
// slices (as there is no
slicing)
libdar::crypto_blowfish,
//
the blowfish strong encryption
//
will be used
"", // as the password is
not given here
//
(empty string is not a valid password)
//
it will be asked interactively to the
// user through the dialog
// user_interaction object
20480, // the block of encryption will be
// 20 kbytes
libdar::not_mask(libdar::simple_mask("*gz")),
//
all files will be compressed except
// those ending by "gz"
800, // file which size is below
800 bytes
// will not be compressed
false, // all files will be saved whatever is
// the nodump flag value
false, // as we make a full backup this
//
"ignore_owner" parameter is useless
0, // hourshift is
useless here as we make
// a
full backup
false, // we will make a real archive not a
// dry-run operation
true, // dar will set back the access
time of
// file it opens, which will
change the
// ctime date of theses files
false, // we may change of filesystem if
// necessary
ret); // this value is returned by libdar
|
// creating an archive is simple, it is just
// calling the "create" constructor of the archive
// class. It may be used for full or differential
// archive. But we will see differential later.
// my_arch should have been an object instead of a
// pointer to an archive object.
libdar::user_interaction_callback dialog =
libdar::user_interaction_callback(ptr1, ptr2, ptr3);
// where ptr1, ptr2 and ptr3 are three callback
// functions.
libdar::statistics ret;
// we will see this structure a bit further
U_16 exception,
std::string except_msg;
libdar::archive *my_arch
=
libdar::create_archive_noexcept(dialog,
"/home", // saving all under this "root"
"/tmp", // where the slices will go
NULL, // we do a full
backup for now
libdar::not_mask(simple_mask("*~", true)),
//
we don't save the files ending with
// '~' this does not concern
directories
libdar::bool_mask(true),
//
all directories and files not rejected
//
by the previous mask are saved
"my_archive",
//
the basename of the slices
"dar", // dar's slice extensions
true, // we allow slice overwriting
true, // but ask to be warned in such
occurence
false, // we don't want a verbose output
false, // nor we want to pause between slices
true, // rejected directory will be
saved as
// empty (no directory is
rejected so
// here there is no
importance for that
// parameter)
libdar::gzip // the archive will be compressed
// using gzip
9, // at maximum
compression level
0, // no slicing is
done,
0, // so the first slice
size must also be
//
set to zero
true, // root EA will be saved if present
true, // user EA too
"", // no script will be
executed between
// slices (as there is no
slicing)
libdar::crypto_blowfish,
//
the blowfish strong encryption
//
will be used
"", // as the password is
not given here
//
(empty string is not a valid password)
//
it will be asked interactively to the
// user through the dialog
// user_interaction object
20480, // the block of encryption will be
// 20 kbytes
libdar::not_mask(libdar::simple_mask("*gz")),
//
all files will be compressed except
// those ending by "gz"
800, // file which size is below
800 bytes
// will not be compressed
false, // all files will be saved whatever is
// the nodump flag value
false, // as we make a full backup this
//
"ignore_owner" parameter is useless
0, // hourshift is
useless here as we make
// a
full backup
false, // we will make a real archive not a
// dry-run operation
true, // dar will set back the access
time of
// file it opens, which will
change the
// ctime date of theses files
false, // we may change of filesystem if
// necessary
ret, // this value is returned by
libdar
exception, // thisgives the status of the call
except_msg); // and in case of error the cause.
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
When creating an archive, the created
archive object can be used only as reference for a isolation or for
differential backup. You cannot use it for restoration, listing,
comparison, because the underlying file descriptors are openned in
write only mode. An implementation which uses file descriptors in
read-write access is not possible and is not a good idea anyway. Why ?
Because, for example, if you want to test your just created archive,
using the just created object would make the testing rely on
information stored in virtual memory (the archive contents, the data
location of a file, etc.), not in the file archive. If some corruption
occured in the file you would not notice it.
So to totally complete the archive creation we must destroy the archive
object we have just created, which have also as consequence to close
any file descriptor used by the object :
delete my_arch;
|
libdar::close_archive_noexcept(my_arch, exception,
except_msg);
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
6 - Testing the archive we have created
So, as explained previously, we must create a new archive object but
this time with the "read" constructor:
my_arch = new
libdar::archive(dialog,
"/tmp", // where is the
archive
"my_archive", // slice name
"dar", // dar's
archive extensions
libdar::crypto_blowfish,
"",
20480, // theses last three are
for encryptions
"", // not used
as we didn't gave "-" as
"", // slice
name
"", // no
command executed for now
false); // no verbose output
|
my_arch =
libdar::open_archive_noexcept( dialog,
"/tmp", // where is the
archive
"my_archive", // slice name
"dar", // dar's
archive extensions
libdar::crypto_blowfish,
"",
20480, // theses last three are
for encryptions
"", // not used
as we didn't gave "-" as
"", // slice
name
"", // no
command executed for now
false, // no verbose output
exception,// this gives the status of the call
except_msg); // and in case of
error the
// cause of the error
i f(exception !=
LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
Now that we have openned the archive we can perform all operation on
it, let's thus start by testing the archive coherence:
ret = my_arch->op_test(dialog,
libdar::bool_mask(true),
// all files are tested
libdar::bool_mask(true),
//no directory is prune
false); // no verbose output
|
ret = libdar::op_test_noexcept( dialog,
my_arch, // the archive
to test
libdar::bool_mask(true),
// all files are tested
libdar::bool_mask(true),
//no directory is prune
false, // no verbose
output
exception,// this gives the status of the call
except_msg); // and in case of
error the
// cause of the error
i f(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
We have tested the archive, but have
not yet seen the libdar::statistics variable. It has been used to
create an archive as well as here to test it. This structure
reports the number of files treated, as well as the number files with
error and the type of error. You can have a look at the API reference
guide for more information about the use of different field. Here is an
example, which relies on the class deci to display the
value of an infinint variable:
// we need the class deci to display the value of
an infinint:
#include "deci.hpp"
std::cout << std::string("Number of file treated :")
<< libdar::deci(ret.treated).human() << std::endl;
// or much more simple (but totally equivalent):
std::cout << std::string("Number of file treated :")
libdar::<< ret.treated << std::endl;
|
Note that the use of the class deci
may throw exceptions (in case of lack of memory, for example), and
there is actually no wrapper available to trap the exceptions that may
be thrown by the class deci.
So you have to protect the code using a
try {} catch {}
statment.
7 - listing archive contents
The simple way:
my_arch->op_listing(dialog,
false, // not a verbose output
false, // not using the tar-like format
bool_mask(true),
// all filenames are listed
false);// do not filter unsaved files
|
libdar::op_test_listing( dialog,
false,
// not a verbose output
false, // not using the tar-like format
bool_mask(true),
// all filenames are listed
false,// do not filter unsaved
files
exception,// this gives the status of the call
except_msg); // and in case of
error the
// cause of the error
i f(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
endl;
|
The listing will be done calling the
warning() method of the
dialog object once for each file to
list. This may not be very interesting because you will have just a
string for each file, and it would require some parsing if you would
like to split the listing in column, or display only filenames first
and have other information available to the user in another way. The
solution to this problem is to change the
user_interaction object.
The
user_interaction class
has a
listing() method
which provides as much arguments as different information to display:
- filename,
- permission,
- user,
- group,
- file size,
- last modification date,
- if the file is a directory
- if the file has children or is an empty dir
- file type
- flag about saved data / saved EA / compression used
In the
user_interaction class
(a pure virtual class), the
listing()
method is not a pure virtual method, so you are not obliged to
overwrite it, but it has just an empty implemtation so it does nothing.
You understand now that, by default, this method is not used. To
activate it, you must call
set_use_listing(true) protected method and of course you will
have to ovewrite the
listing()
method to have a less silly behavior:
// here follows the definition of our own implementation of
// of a user_interaction class
class my_user_interaction : public user_interaction
{
public :
// the inherited pure virtual methods we must define
// as seen at the beginning of this tutorial:
void pause(const std::string & message);
void
warning(const std::string & message);
std::string get_string(const
std::string & message, bool echo);
user_interaction *clone() const;
// we can overwrite this method to have splitted
fields for listing:
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);
// but it will not get used by libdar unless
we call the protected method set_use_listing()
// for example this can be done in the class
constructor :
my_user_interaction() { set_use_listing(true); };
};
|
Now assuming we have implemented the listing() method in my_user_interaction class, calling op_listing() exactly as we did in
the simple way, but just replacing the dialog object by one of the my_user_interaction class, would
make this listing() method
called for each file to be listed, in place of the warning() method.
As seen at the beginning of this
tutorial too, there is a children class of user_interaction based on callback
functions which is called user_interaction_callback.
The listing() method must
also be activated here. This is done automatically when you give a
callback function to the object, thanks to the set_listing_callback()
method :
// our mandatory callback functions:
void warning_callback(const std::string &x, void *context)
{
....
}
bool answer_callback(const std::string &x, void *context)
{
....
}
std::string string_callback(const std::string &x, bool echo, void
*context)
{
....
}
// let's build a user_interaction_callback object:
libdar::user_interaction_callback dialog =
libdar::user_interaction_callback(&warning_callback,
&answer_callback, &string_callback, NULL);
// at this point our dialog object is perfectly
operational for listing
// but libdar will call the warning_callback function to
list the archive
// contents
// a new callback function for listing :
void listing_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)
{
....
}
dialog.set_listing_callback(&listing_callback);
// now libdar will call the listing_callback function when
we
// will use this dialog
object for listing the archive contents.
|
8 - comparing with filesystem
comparison is realized by calling the op_diff method
of the class archive.
ret = my_arch->op_diff(dialog,
"/home", // what directory to take
// as root we shall
// compare the archive
// contents to
libdar::bool_mask(true),
libdar::bool_mask(true),
false, // no verbose output
true, // comparing root EA
true, // comparing user EA
false, // do not ignore ownership
// differences
false); // do not set back atime
// of read files
|
ret = libdar::op_diff_noexcept(dialog,
my_arch, // the archive to use
"/home", // what directory to take
// as root we shall
// compare the archive
// contents to
libdar::bool_mask(true),
libdar::bool_mask(true),
false, // no verbose output
true, // comparing root EA
true, // comparing user EA
false, // do not ignore ownership
// differences
false, // do not set back atime
// of read
files exception, // this gives the
// status of the call
except_msg); // and in case of
// error the cause of the
//
error
i f(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
simple, no ?
9 - restoring files
restoration of files is done by calling the op_extract
method of archive class.
ret = my_arch->op_extract(dialog,
"/tmp", // where to restore files to
libdar::bool_mask(true),
// restore any filename
libdar::simple_path_mask("denis/.tcshrc",
true),
// but only restore
// this file or directory
false,// overwriting not allowed
true, // warn before overwriting
//
useless here, not overwritting
true, //
verbose output
true, //
remove files marked as deleted
// useless here too, as verwriting
//
has been disabled
false,// restore even if a file is more
// recent, (useless here too)
true, // restore root EA
true, //
restore user EA
false,//
restore directory structure
false,// restore ownership
false,//
don't warn if a file to be
//
removed has a type that does
//
the type of the orginal file
0, // no daylight saving consideration
false);
// not a dry-run execution
|
ret = libdar::op_extract_noexcept( dialog,
my_arch, // the archive
to test
"/tmp",
// where to restore files to
libdar::bool_mask(true),
// restore any filename
libdar::simple_path_mask("denis/.tcshrc",
true),
// but only restore
// this file or directory
false,// overwriting not allowed
true, // warn before overwriting
// useless here, not
overwritting
true, // verbose output
true, // remove files marked as deleted
// useless here too, as verwriting
// has been disabled
false,// restore even if a file is
more
// recent, (useless here too)
true, // restore root EA
true, // restore user EA
false,// restore directory structure
false,// restore ownership
false,// don't warn if a file to be
// removed has a type
that does
// the type of the
orginal file
0, // no daylight
saving consideration
false,// not a dry-run execution
exception,// this gives the status of the call
except_msg); // and in case of
error the
// cause of the error
i f(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
10 - isolating the catalogue
OK, I know, catalogue is
not an english word (one would rather write catalog), but that's the name of
the C++ class used in
libdar, so we will keep using it here. Note that you don't have to
directly access to this class.
Isolating the catalogue creates a new
archive that only contains the list of files and their attributes
(ownership, dates, size, etc.), but no data and no EA are stored in it.
It is very similar to the same archive one get if he would made a
differential backup of a filesystem that has not changed since the
archive of reference. The usage is very similar to the archive
creation, but it uses a different constructor that has less arguments :
libdar::archive *my_cat = new libdar::archive(dialog,
"/tmp", // where is saved the
// extracted catalogue
my_arch, // the archive of reference
// is the one we have been
// playing with previously
"my_catalogue", // slice name
"dar", // file extension
true, // allow slice overwriting
true, // but warn before
true, // verbose output
true, // pause between slices
libdar::bzip2, // compression
9, // max compression level
3000, // cut in slices of 3000 Bytes
3000, // first slice size is the same
"echo slice %p/%b.%n.%e created",
// the script to run after
// slice creation
libdar::crypto_none, // no encryption
"", // unused password
0, // unused crypto size
false); // not a dry-run execution
|
libdar::archive *my_cat =
libdar::op_isolate_noexcept(dialog,
"/tmp", // where is saved the
// extracted catalogue
my_arch, // the archive of reference
// is the one we have been
// playing with previously
"my_catalogue", // slice name
"dar", // file extension
true, // allow slice overwriting
true, // but warn before
true, // verbose output
true, // pause between slices
libdar::bzip2, // compression
9, // max compression level
3000, // cut in slices of 3000 Bytes
3000, // first slice size is the same
"echo slice %p/%b.%n.%e created",
// the script to run after
// slice creation
libdar::crypto_none, // no encryption
"", // unused password
0, // unused crypto size
false, // not a dry-run execution
exception,
// this gives
the status
// of the call
except_msg);
// and in
case of error the
// cause of the error
i f(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
Now we have two archive objects.
my_arch is a read-only object
created by the "read" constructor. You can do any operations with it,
like file restoration, file comparison, archive testing, as we have
done in the previous paragraphs. The second archive object is
my_cat which is a write only
object. It can only be used as reference for another backup (a
differential backup) or as a reference for a subsequent catalogue
isolation (which would just clone the already isolated catalogue
object, here).
Note, that an isolated catalogue can be tested, compared with
filesystem, and even you can try to restore files from it. But as there
is no data associated with files contents, dar will not restore any
file from it, of course. So for now we will just destroy the extracted
catalogue object, for all its file descriptors to be closed:
delete my_cat;
|
close_archive_noexcept (my_cat, exception,
except_msg);
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
and we keep the
my_arch
object for our last operation :
11 - creating a differential backup
This operation is the same as the
first one we did (archive creation). You have maybe noted that an
argument was set to NULL,
here we will give it the value of my_arch
which means that my_arch
will become the archive of reference for the archive we will create. If
we had not destroyed my_cat above,
we could have been using it in place of my_arch for exactly the same result.
libdar::archive *my_other_arch =
new libdar::archive(dialog,
"/home", // saving all under this "root"
"/tmp", // where the slices will go
my_arch,
// differential backup
not_mask(simple_mask("*~", true)),
//
we don't save the files ending with
// '~' this does not concern
directories
bool_mask(true),
//
all directories and files not rejected
//
by the previous mask are saved
"my_archive",
//
the basename of the slices
"dar", // dar's slice extensions
true, // we allow slice overwriting
true, // but ask to be warned in such
occurence
false, // we don't want a verbose output
false, // nor we want to pause between slices
true, // rejected directory will be
saved as
// empty (no directory is
rejected so
// here there is no
importance for that
// parameter)
libdar::gzip // the archive will be compressed
// using gzip
9, // at maximum
compression level
0, // no slicing is
done,
0, // so the first slice
size must also be
//
set to zero
true, // root EA will be saved if present
true, // user EA too
"", // no script will be
executed between
// slices (as there is no
slicing)
libdar::crypto_blowfish,
//
the blowfish strong encryption
//
will be used
"", // as the password is
not given here
//
(empty string is not a valid password)
//
it will be asked interactively to the
// user through the dialog
// user_interaction object
20480, // the block of encryption will be
// 20 kbytes
not_mask(simple_mask("*gz")),
//
all files will be compressed except
// those ending by "gz"
800, // file which size is below
800 bytes
// will not be compressed
false, // all files will be saved whatever is
// the nodump flag value
false, // as we make a full backup this
//
"ignore_owner" parameter is useless
0, // hourshift is
useless here as we make
// a
full backup
false, // we will make a real archive not a
// dry-run operation
true, // dar will set back the access
time of
// file it opens, which will
change the
// ctime date of theses files
false, // we may change of filesystem if
// necessary
ret); // this value is returned by libdar
|
libdar::archive *my_other_arch
=
libdar::create_archive_noexcept(dialog,
"/home", // saving all under this "root"
"/tmp", // where the slices will go
my_arch, //
differential backup
not_mask(simple_mask("*~", true)),
//
we don't save the files ending with
// '~' this does not concern
directories
bool_mask(true),
//
all directories and files not rejected
//
by the previous mask are saved
"my_archive",
//
the basename of the slices
"dar", // dar's slice extensions
true, // we allow slice overwriting
true, // but ask to be warned in such
occurence
false, // we don't want a verbose output
false, // nor we want to pause between slices
true, // rejected directory will be
saved as
// empty (no directory is
rejected so
// here there is no
importance for that
// parameter)
libdar::gzip // the archive will be compressed
// using gzip
9, // at maximum
compression level
0, // no slicing is
done,
0, // so the first slice
size must also be
//
set to zero
true, // root EA will be saved if present
true, // user EA too
"", // no script will be
executed between
// slices (as there is no
slicing)
libdar::crypto_blowfish,
//
the blowfish strong encryption
//
will be used
"", // as the password is
not given here
//
(empty string is not a valid password)
//
it will be asked interactively to the
// user through the dialog
// user_interaction object
20480, // the block of encryption will be
// 20 kbytes
not_mask(simple_mask("*gz")),
//
all files will be compressed except
// those ending by "gz"
800, // file which size is below
800 bytes
// will not be compressed
false, // all files will be saved whatever is
// the nodump flag value
false, // as we make a full backup this
//
"ignore_owner" parameter is useless
0, // hourshift is
useless here as we make
// a
full backup
false, // we will make a real archive not a
// dry-run operation
true, // dar will set back the access
time of
// file it opens, which will
change the
// ctime date of theses files
false, // we may change of filesystem if
// necessary
ret, // this value is returned by
libdar
exception, // thisgives the status of the call
except_msg); // and in case of error the cause.
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
As previously my_other_arch is a write only object that we won't need
anymore. So we destroy it:
delete my_other_arch;
|
libdar::close_archive_noexcept(my_other_arch,
exception,
except_msg);
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
So, we are at the end of the tutorial, but still remains an object we
need to destroy to cleanly release the memory used :
delete my_arch;
|
libdar::close_archive_noexcept(my_arch, exception,
except_msg);
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occured: " << except_msg
<<
std::endl;
|
For more detailed information about the API you have the API reference
guide, built from source code by Doxygen.