LIBDAR
APPLICATION INTERFACE
TUTORIAL
for API version 3.0.x
Presentation
The Libdar library has been built from source code originally
located directly in the dar command line
application. Libdar provides a complete abstraction for handling Disk
ARchive (dar)'s archives. The general operations provided are:
- archive creation,
- file extraction,
- archive listing,
- archive testing,
- archive comparison,
- catalogue isolation.
Note that Disk ARchive and libdar
have been released under the Gnu
General Public License (GPL). All code linked to libdar
(statically or dynamically), must
also be covered by the GPL.
This tutorial will show you how to
use the libdar API. As dar
now also uses this API, looking at it's code may also provide a good
illustration. The file dar_suite/dar.cpp
is the primary consumer.
The sample code provided here is
solely illustrative and is not guaranteed to compile. More detailed API
documentation is contained in the source code and can be compiled to
the doc/html directory using Doxygen. The API reference is also
available
online.
Let's Start
Conventions
Language
Dar and libdar are written in C++, and so is the libdar API. While
written in C++, libdar is easily usable with both C and C++ code.
Access from other languages can be provided by specific bindings. I
would only say that you are welcome to provide the necessary bindings
yourself. :-)
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 shown below, you can
explicitly use the namespace in front of libdar objects :
libdar::get_version(....);
|
Exceptions or no Exceptions
The library can be used with or
without exceptions. For each example we will see sample code for both
methods. To the left is with exceptions, to the right without:
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 message string describing the message (in human
language). The type of the error is defined by the class of the
exception. The possible exception types follow:
class
libdar::Egeneric
|
the parent class of all
exceptions (a pure virtual class)
|
class libdar::Ememory
|
memory has been exhausted
|
class libdar::Ebug
|
signals a bug, which is
triggered when reaching some code that should never be reached
|
class libdar::Einfinint
|
arithmetic error detected when
operating on infinint
|
class libdar::Elimitint
|
a limitint overflow is detected,
indicating the maximum value of the limitint has been exceeded
|
class libdar::Erange
|
signals a range error
|
class libdar::Edeci
|
signals conversion problem
between infinint and string (decimal representation)
|
class libdar::Efeature
|
a requested feature is not (yet)
implemented
|
class libdar::Ehardware
|
hardware problem is found
|
class libdar::Euser_abort
|
signals that the user has
aborted the operation
|
class libdar::Edata
|
an error concerning the treated
data has been encountered
|
class libdar::Escript
|
the script executed between
slices returned an error code
|
class libdar::Elibcall
|
signals an error in the
arguments given to a libdar call of the API
|
class libdar::Ecompilation
|
a requested feature has not been
activated at compilation time
|
1 - First we *must* check the libdar version
// we'll want 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's sake
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'll want 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's sake
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've dynamically linked with is compatible with the features you will
be using. The major number must be the same, for no compatibility is
assured between two libdar versions of different major numbers. While
run-time compatibility is assured between medium numbers, the medium
number must be greater or equal to the one used at compilation time to
be sure that all the features you want are available in the libdar
library you dynamically linked with. Changes between minor versions
correspond to bug fixes and is not to imply any API change, thus no
constraints are present there.
- the get_version* call, as well as
returning version information, does important initialization tasks for
libdar. If not called first, the libdar library will not initialized
properly and its behavior will be unpredictable. Note that you may call
get_version* several time if you wish, using any override.
2 - Let's see the available features
once we have called a function of the get_version*
function 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. It's possible to display the available libdar
features or to terminate if you don't find a desired feature. However,
verifying that features are available is not strictly necesary because
libdar will tell you if an operation you call requires a feature that
has not been activated by throwing an Ecompile exception or returning
the LIBDAR_ECOMPILATION error
code.
3 -User interaction
The generic user_interaction
class
To be able to report messages to the user and prompt for feedback a
special class called user_interaction has been
introduced. Simply put, user_interaction
is a virtual class which you can derive to provide user interaction (a
GUI's graphical interaction, for example). There are four methods whose
prototypes you must override:
void pause (const std::string
&message);
this method is
called by libdar when the library needs a yes or no ("continue" or
"kill")
answer to a question, which is provided by the string message.
The question posed by pause() must be answered by returning
"true" or throwing a Euser_abort
exception if the user refused the proposition. Don't
worry about throwing an exception in your code; it will be trapped by
libdar if you don't want to manage exceptions, and are using libdar in
the "no exception" method. (NOTE: Unfortunately there
is currently no way to send
+ back a cancel signal without throwing an exception. For next API
version a new method called pause2() will allow you to answer
"true" or "false" and thus avoid using exceptions)
void warning (const std::string
&message);
libdar calls this
method to display an informational message to the user. It is not
always a warning as the name suggests, but sometimes 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. This is mainly used to get a
password from the user (when no password has been supplied for an
encrypted archive), the echo argument
indicates if the user response should be displayed on the screen
(again, very useful for handling password input). If echo is set to "false" the
implementation of get_string() should
hide the characters typed by the user.
user_interaction * clone
() const;
A deep copy
operation must be implemented here. This is because libdar stores the
reference to the user_interaction
class as a pointer but may want to keep a complete internal copy at
some point. 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
An inherited class from
user_interaction called
user_interaction_callback
provides an implementation of the user interaction based on callback
functions. This allows you to replace 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. The
clone()
method is implemented internally, leaving only the three callback
functions to be implemented. Look at
dar's command line code for a practical example.
dar's user interaction code is
implemented using an instance of
user_interaction_callback
and three static functions in the module
dar_suite/shell_interaction.cpp
Pay attention to 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 method show() wait_for_click() and so on
// 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 its user_interaction object based
on the same
// user_interaction_callback object pointing to the same
functions.
// user_interaction_callback objects can be shared among
different window objects
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 as is
from the constructor to the callback
// functions
|
4 - Masks
Mask are used to define which files
will be considered and which will not. Libdar implements masks as
several classes that all inherit from a virtual class that defines the
way masks are used. This root class is the class mask and
provides the is_covered()
method which libdar uses to determine which files are considered. There
are many different basic masks classes you can use to build fairly
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 subdirectory of mask or is a directory
that contains the specified path itself
|
class libdar::same_path_mask
|
matches if the string is exactly
the
given mask (no wild 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 selected 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 directory that contains
/home/joe, meaning
// "/", "/home", "/jome/joe" and any
subdirectory are matched.
// here, the second argument is also
case sensitivity (so
// "/HoMe" will not be selected 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 selected 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 an
"ou_mask" and would get a silly
// counterpart of m1 (a mask that
matches any files)
libdar::ou_mask m6;
m6.add_mask(m2);
m6.add_mask(m4);
m6.add_mask(m3);
// lastly, the NOT, AND and OR operation
can be used recursively.
// Frankly, it's
possible to have masks reference each other!
libdar::not_mask m7 = m6;
m6.add_mask(m7);
|
Now that you've seen the power of these masks, you should know that in
libdar there are two masks that are required:
- The first mask is used against the
names of all files except directories. It is applied solely to the
names themselves (not the file path). This mask may be any combination
of the masks seen previously; it will only be applied to socket, named
pipes, symbolic links, char or block devices, plain files, but again
not to directories. This way you can filter by file type for save,
restore, list, compare, and compress, and other library operations.
- The second mask is applied to any
file including directories, including the path part of the filename. So
with it you can prune directories, or in any other way restrict the
operation to a particular 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 let's start the real thing:
// creating an archive is simple; it is just
// a matter of calling the "create" constructor
// of the archive class. It may be used for full or
// differential archives. We'll see an example of
// of differential archives later.
// note that while this example uses a pointer to store
// my_arch, it is perhaps better practice to use a plain
// stack object. In your code, use an object instead of
// a pointer to an object under normal circumstances.
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
occurrence
false, // we don't want a verbose output
false, // nor do we want to pause between
slices
true, // rejected directories 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 queried interactively
// 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 with "gz"
800, // files whose sized is below
800 bytes
// will not be compressed
false, // all files will be saved regardless of
// the nodump flag value
false, // as we're making a full backup this
//
"ignore_owner" parameter is useless
0, // hourshift is
useless here as we're making
// 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 cross to other filesystems if
// necessary
ret); // this value is returned by libdar
|
// creating an archive is simple; it is just
// a matter of calling the "create" constructor
// of the archive class. It may be used for full or
// differential archives. We'll see an example of
// of differential archives later.
// note that while this example uses a pointer to store
// my_arch, it is perhaps better practice to use a plain
// stack object. In your code, use an object instead of
// a pointer to an object under normal circumstances.
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
occurrence
false, // we don't want a verbose output
false, // nor do we want to pause between
slices
true, // rejected directories 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 queried interactively
// 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 with "gz"
800, // files whose size is below
800 bytes
// will not be compressed
false, // all files will be saved regardless of
// the nodump flag value
false, // as we're making a full backup this
//
"ignore_owner" parameter is useless
0, // hourshift is
useless here as we're making
// 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 cross to other filesystems if
// necessary
ret, // this value is returned by
libdar
exception, // this gives the status of the call
except_msg); // and in case of error the cause.
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occurred: " << except_msg
<< std::endl;
|
When creating an archive, the created
archive object can be used only as reference for an isolation or for
differential backups. You cannot use it for restoration, listing, or
comparison, because the underlying file descriptors are opened 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 the newly created archive,
using the newly created object would make the test rely on information
stored in virtual memory (the archive contents, the data location of a
file, etc.), not on the file archive itself. If some corruption occurred
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 will also close any file descriptors
used by the object :
delete my_arch;
|
libdar::close_archive_noexcept(my_arch, exception,
except_msg);
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occurred: " << 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 occurred: " << except_msg
<<
std::endl;
|
Now that we have opened the archive we can perform any 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 occurred: " << except_msg
<<
std::endl;
|
We have tested the archive, but have
not yet seen the libdar::statistics variable. It can be used when
creating an archive as well as when testing it. This structure
reports the number of files treated, as well as the number files with
errors and the type of error. You can have a look at the API reference
guide for more information about the uses of different fields. 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 simpler (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 {}
statement.
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 occurred: " << except_msg
<<
endl;
|
By default the library will complete
the listing by calling the
warning()
method of the
dialog object
one time for each file listed. The warning text will consist of a
string for each file with the relevant information in columns that
would need to be parsed if individual information was desired. This may
not be appropriate for you and as such there is another way to get
listing information. This requires a simple reimplementation of the
user_interaction
object.
The
user_interaction class
has a
listing() method
which provides separate arguments for each piece of information that
can be displayed:
- 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
Technical note: You may notice that file type is
not explicitly given as a parameter in the listing method. File type is
available as the first byte of the permissions string. This is standard
POSIX stuff except for an extension: "h" for files hard
linked several times. See man 2 stat for
more information. Note however that in the last arguments of this call,
you may easily know whether a file is a directory or not and whether it
is empty or not.
In the user_interaction class
(a 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 implementation 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 overwrite 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
before, only replacing the dialog
object by one of the my_user_interaction
class. Then this listing()
method will be called for each file to be listed, in place of the warning() method.
As seen at the beginning of this
tutorial, there is a child 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
// use this dialog object
for listing the archive contents.
|
8 - comparing with filesystem
We can compare file in an archive with the filesystem 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 occurred: " << except_msg
<<
std::endl;
|
Simple, no?
9 - restoring files
Restoration of files is done by calling the op_extract
method of class archive.
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 occurred: " << 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 this
class (if you really don't like French).
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 gets if one makes a
differential backup of a filesystem that has not changed since the
creation of a reference archive. 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 the extracted
// catalogue is saved
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 occurred: " << 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 sections. The second archive object is
my_cat which is a write only
object. It can only be used as a 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, if desired, an isolated catalogue can be tested, compared
with the filesystem, and you can even try to restore files from it. But
as there is no data associated with the files contents, dar will not
restore any files from it, of course. So for now we will just destroy
the extracted catalogue object, so that all its file descriptors are
closed:
delete my_cat;
|
close_archive_noexcept (my_cat, exception,
except_msg);
if(exception != LIBDAR_NOEXCEPT)
std::cout << "an error occurred: " << 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). Perhaps you have noted that an
argument was set to NULL.
Here we will pass it my_arch which
means that my_arch will
become the reference archive for the archive we will create. If we had
not destroyed my_cat above,
we could have used 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
occurrence
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
occurrence
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 occurred: " << 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 occurred: " << except_msg
<<
std::endl;
|
So, we are at the end of the tutorial. We still have 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 occurred: " << except_msg
<<
std::endl;
|
For more detailed information about the API you can build the API
documentation from the source code using Doxygen.
12 - Compilation & Linking
Compilation
All the symbols found in the libdar API are defined via <dar/libdar.h>
so you should only need to include this header.
> cat my_prog.cpp
#include <dar/libdar.h>
main()
{
libdar::get_version(...);
...
}
> gcc -c my_prog.cpp
|
Linking
Of course, you need to link your program with libdar. This is done by
adding -ldar
:
> gcc -ldar my_prog.o -o
my_prog
|
Libdar's different flavors
Well, all the compilation and linking steps described above assume you
have a "full" libdar library.
Beside the full (alias infinint) libdar flavor, libdar
also comes in 32 and 64 bits
versions. In theses last ones, in place of internally relying on a
special type (which
is a C++ class called infinint)
to
handle arbitrary large integers, libdar32 relies on 32 bits integers
and
libdar64 relies on 64 bits integers (there are limitations which are
described in doc/LIMITATIONS). But all theses libdar version (infinint,
32bits, 64bits) have the same
interface and must be used the same way, except for compilation and
linking.
Theses different libdar versions can coexist on the same system, they
share the same include files. But the MODE macro must be set to 32
or 64 when compiling for linking with libdar32 or libdar64
respectively. The MODE macro defines the way the "class infinint" type is
implemented in libdar, and thus changes the way the libdar headers
files are interpreted by the compiler.
>
cat my_prog.cpp
#include <dar/libdar.h>
main()
{
libdar::get_version(...);
...
}
> gcc -c -DMODE=32
my_prog.cpp
> gcc -ldar32 my_prog.o -o
my_prog
|
and replace 32 by 64 to link with libdar64.
Thanks
I would like to thank Wesley Leggette for having given his feedback and
having done grammar corrections to this document.
Regards,
Denis Corbin.