pax_global_header 0000666 0000000 0000000 00000000064 12706673016 0014522 g ustar 00root root 0000000 0000000 52 comment=33e813de4d0ec108a26816c4cb6c4630f68cc005
backup2l-1.6/ 0000775 0000000 0000000 00000000000 12706673016 0013073 5 ustar 00root root 0000000 0000000 backup2l-1.6/.gitignore 0000664 0000000 0000000 00000000034 12706673016 0015060 0 ustar 00root root 0000000 0000000 *~
debian/tmp
debian/files
backup2l-1.6/LICENSE 0000664 0000000 0000000 00000043177 12706673016 0014114 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
backup2l-1.6/README.md 0000664 0000000 0000000 00000017412 12706673016 0014357 0 ustar 00root root 0000000 0000000 # *backup2l* - a low-maintenance backup/restore tool
by Gundolf Kiefer, 2001-2015
## Description
*backup2l* is a lightweight command line tool for generating, maintaining and restoring backups on a mountable file system (e. g. hard disk). The main design goals are are low maintenance effort, efficiency, transparency and robustness. In a default installation, backups are created autonomously by a cron script.
*backup2l* supports hierarchical differential backups with a user-specified number of levels and backups per level. With this scheme, the total number of archives that have to be stored only increases logarithmically with the number of differential backups since the last full backup. Hence, small incremental backups can be generated at short intervals while time- and space-consuming full backups are only sparsely needed.
The restore function allows to easily restore the state of the file system or arbitrary directories/files of previous points in time. The ownership and permission attributes of files and directories are correctly restored.
An open driver architecture allows to use virtually any archiving program as a backend. Built-in drivers support .tar.gz, .tar.bz2, or .afioz files. Further user-defined drivers can be added as described in the supplied sample configuration file 'first-time.conf'.
An integrated split-and-collect function allows to comfortably transfer all or selected archives to a set of CDs or other removable media.
All control files are stored together with the archives on the backup device, and their contents are mostly self-explaining. Hence, in the case of an emergency, a user does not only have to rely on the restore functionality of *backup2l*, but can - if necessary - browse the files and extract archives manually.
For deciding whether a file is new or modified, *backup2l* looks at its name, modification time, size, ownership and permissions. Unlike other backup tools, the i-node is not considered in order to avoid problems with non-Unix file systems like FAT32.
## Screenshots
**a) Generating a backup: mail received from cron daemon**
The monitored area covers 26803 (=23733+3053) files and directories and over 2.2 GB of data. Look at the time stamps!
```
backup2l v0.9 by Gundolf Kiefer
Tue Nov 6 07:58:00 CET 2001
Mounting /disk2...
Running pre-backup procedure...
writing dpkg selections to /root/getselections.log...
Removing old backups...
Preparing differential level-3 backup based on ...
657 / 23745 file(s), 94 / 3058 dir(s), 63945 / 2332639 KB (uncompressed)
skipping: 498 file(s), 14 dir(s), 3144527 KB (uncompressed)
Creating archive...
Checking TOC of tar file (< real file, > archive entry)...
Creating check file for ...
Tue Nov 6 07:58:41 CET 2001
Summary
=======
Archive Date | Size (KB) | Skipped Files+Dirs | New Obs. | Errors
------------------------------------------------------------------------------
all.1 2001-07-15 | 1475248 | 620 17795 |17795 0 | 45
all.101 2001-07-27 | 235720 | 617 17002 | 812 1605 | 2
all.102 2001-08-19 | 210648 | 626 23446 | 7077 633 | 1
all.103 2001-08-30 | 125396 | 670 23796 | 1963 1613 | 0
all.104 2001-09-07 | 203880 | 669 25597 | 5353 3552 | 1
all.105 2001-09-20 | 87076 | 409 24333 | 2192 3456 | 6
all.106 2001-10-01 | 88492 | 409 27611 | 4591 1313 | 1
all.107 2001-10-12 | 67624 | 409 26589 | 1194 2216 | 0
all.108 2001-10-21 | 111580 | 506 26553 | 1351 1387 | 0
all.1081 2001-10-21 | 948 | 506 26554 | 70 69 | 0
all.1082 2001-10-22 | 164 | 506 26554 | 90 90 | 0
all.1083 2001-10-23 | 46680 | 506 27814 | 1465 205 | 0
all.1084 2001-10-24 | 29672 | 506 28578 | 1111 347 | 0
all.1085 2001-10-25 | 28272 | 506 26869 | 282 1991 | 0
all.1086 2001-10-26 | 45000 | 506 26866 | 314 317 | 0
all.1087 2001-10-27 | 33136 | 506 27162 | 610 314 | 0
all.1088 2001-10-28 | 12280 | 506 27645 | 673 190 | 0
all.11 2001-10-29 | 822056 | 506 27725 |13582 3652 | 8
all.1101 2001-10-30 | 20888 | 506 23707 | 858 4876 | 0
all.1102 2001-10-31 | 11336 | 512 26807 | 3372 272 | 0
all.1103 2001-11-05 | 312 | 512 26786 | 173 194 | 0
all.1104 2001-11-06 | 49592 | 512 26803 | 751 734 | 0
Filesystem Size Used Avail Use% Mounted on
/dev/hde1 5.8G 3.5G 2.3G 61% /disk2
Unmounting /disk2...
```
**b) Restoring the directory */home/home/gundolf/prog/* from snapshot **:**
```
lilienthal:/scratch# backup2l -t 1102 -r /home/gundolf/prog/
backup2l v0.9 by Gundolf Kiefer
Mounting /disk2...
Active files in : 246
found in all.1102: 119 ( 127 left)
found in all.1101: 7 ( 120 left)
found in all.11: 120 ( 0 left)
Restoring 35 directories...
Restoring files...
all.11: 120 file(s)
all.1101: 7 file(s)
all.1102: 119 file(s)
Unmounting /disk2...
lilienthal:/scratch#
```
## Installation (from main source directory)
* Debian Linux: Type 'debuild' to get a binary deb archive.
* Others: Run './install-sh' or 'sh install-sh' and follow the instructions.
## Release Notes
Old releases prior to 1.5 can be downloaded from http://sourceforge.net/projects/backup2l/.
Releases from 1.5 upwards are git-tagged accordingly.
**Version 1.4**
With some help from Joe Auricchio , backup2l should
now run under Mac OS X 10.3 (but may still be unstable!). The following
known restrictions apply:
- installing the GNU findutils, e. g. from fink (fink.sf.net),
in particular the GNU versions of 'find' and 'xargs'.
- If no GNU version of sed is installed, the FILTER_* commands in the
beginning of "backup2l" have to be changed to use per for filtering
(just uncomment the respective lines there).
- In some cases, the printf "%s" command of 'find' always returns 0
instead of the file size. This leads to incorrect size reports.
- The date in the summary output is broken unless GNU date is installed.
Binaries for the four GNU programs which Macintosh users need to install
before using backup2l (find, xargs, sed and date) may be found at
http://luddite.cst.usyd.edu.au/~jason/macos-gnu-utils-for-backup2l.tgz
(Jason Grossman ).
**Version 1.1**
An new open driver architecture allows to use virtually any archiving
program as a backend. Built-in drivers support .tar.gz, .tar.bz2, or
.afioz files. Further user-defined drivers can be added as described in
the sample configuration file 'first-time.conf'.
Due to major code changes, this release may be unstable. The latest
stable release is 1.01. Archives created by the default driver
("DRIVER_TAR_GZ") are fully compatible with backup2l 1.01.
**Versions 0.9 & 0.91**
Some versions of find (e. g. 4.1.7.) seem to have a bug so that printf does
not produce leading 0's if requested. As a result, the .list.gz, .new.gz and
.obsolete.gz files may contain entries like
4 11/11/01 02:07:17 0. 0 0777 /var/squid/some_file
instead of
4 11/11/01 02:07:17 0000.0000 0777 /var/squid/some_file
In backup2l 0.91, a workaround is implemented to guarantee the correct (2nd)
format. Two issues have to be considered:
a) Without leading 0's, a restore operation may fail.
b) Backups are always created correctly. However, when switching to 0.91,
differential backups may be larger than necessary.
It is recommended either to modify incorrect .list.gz, .new.gz and
.obsolete.gz files manually or to purge the respective archives.
backup2l-1.6/backup2l 0000775 0000000 0000000 00000121425 12706673016 0014531 0 ustar 00root root 0000000 0000000 #!/bin/bash
# backup2l --- low-maintenance backup tool
# Copyright (c) 2001-2009 Gundolf Kiefer
# This program is free software; you can redistribute 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, write to the Free Software
# Foundation, Inc.; 59 Temple Place, Suite 330;
# Boston, MA 02111-1307, USA.
##################################################
# Set global variables
VER=1.6-pre2
# The following variable defines the commonly required tools. Additional tools
# may be required by special functions, e. g. md5sum by the check functions.
# Tool requirements are check in the do_* and show_* functions.
COMMON_TOOLS="date find grep gzip gunzip sed awk mount umount"
# The following variables define the format of *.list files. Modify at your own risk.
FORMAT="%8s %TD %.8TT %04U.%04G %04m %p" # format for *.list files
FILTER_NAME="sed 's#^\([-:. 0-9]*/\)\{3\}#/#'"
# sed command for extracting names from .list, .new, ... files
# (removes everything up to the 3rd "/"; two are contained in the time field)
FILTER_CHOWN="sed 's#^\( *[^ ]\+\)\{3\} *0\{0,3\}\([0-9]\+\)\.0\{0,3\}\([0-9]\+\) \+\([0-9]*\)\{4\} \+/\(.*\)\$#\2:\3 \"\5\"#'"
# sed command for extracting ownerships and names from .list files
FILTER_CHMOD="sed 's#^\( *[^ ]\+\)\{4\} *\([0-9]\{4\}\) \+/\(.*\)\$#\2 \"\3\"#'"
# sed command for extracting permissions and names from .list files
FILTER_UNIFY_NAME="sed 's#\\\\[0-7]\{3\}#?#g; s#[^a-zA-Z0-9_ .$%:~/=+\#\-]#?#g'"
# replaces special and escaped characters by '?';
# only used when checking TOCs of fresh backup archives in order to avoid false alarms
# The following alternative (donated by Christian Ludwig ) is slightly more precise,
# but requires perl to be installed:
#FILTER_UNIFY_NAME="perl -n -e 's{\\\\(\d{3})}{chr(oct(\$1))}eg; print;'"
# On systems without GNU sed >= 3.0.2 such as Mac OS X, try the following alternatives...
# ... settings (donated by Joe Auricchio ).
#FILTER_CHOWN="perl -pe 's#^( *[^ ]+){3} *0{0,3}([0-9]+).0{0,3}([0-9]+) +([0-9]*){4} +/(.*)\$#\$2.\$3 \"\$5\"#'"
#FILTER_CHMOD="perl -pe 's#^( *[^ ]+){4} *([0-9]{4}) +/(.*)\$#\$2 \"\$3\"#'"
#COMMON_TOOLS="$COMMON_TOOLS perl"
##################################################
# Misc. helpers
require_tools ()
{
local NOT_AVAIL=""
for TOOL in $@; do
if [ "`which $TOOL 2> /dev/null`" == "" ]; then NOT_AVAIL="$NOT_AVAIL $TOOL"; fi
done
if [[ "$NOT_AVAIL" != "" ]]; then
echo "ERROR: The following required tool(s) cannot be found: $NOT_AVAIL"
exit 3
fi
}
get_bid_of_name ()
{
local SUFFIX=${1#$VOLNAME.}
echo ${SUFFIX%%.*}
}
get_last_bid ()
{
if [ -f $VOLNAME.1.list.gz ]; then
REV_ARCH_LIST=(`ls $VOLNAME.*.list.gz | sort -r`)
get_bid_of_name ${REV_ARCH_LIST[0]}
else
echo "0" # default if no archives exist yet
fi
}
get_base_bid ()
{
local BID=$(( $1 - 1 ))
echo ${BID%%+(0)}
}
expand_bid_list ()
{
local SUFFIX="$1"
shift
local ARCH_LIST=""
local BID ARCH
for BID in "$@"; do
ARCH_LIST="$ARCH_LIST `ls -1 $BACKUP_DIR/$VOLNAME.$BID.$SUFFIX 2> /dev/null`"
done
local BID_LIST=""
local ARCH
for ARCH in $ARCH_LIST; do
BID_LIST="$BID_LIST `get_bid_of_name ${ARCH#$BACKUP_DIR/}`"
done
echo $BID_LIST
}
do_symlink ()
{
# tries to symlink & copies if not possible (e. g. on FAT32/Samba file systems)
ln -s $@ &> /dev/null ||
cp -af $@
}
readable_bytes_sum ()
{
awk -v UNIT=$1 '
{ B += $1 }
END {
KB=B / 1024.0;
MB=KB / 1024.0;
GB=MB / 1024.0;
if ((GB>=1.0 && UNIT=="") || UNIT=="G" || UNIT=="g") print sprintf("%.1fG", GB);
else if ((MB>=1.0 && UNIT=="") || UNIT=="M" || UNIT=="m") print sprintf("%.1fM", MB);
else if ((KB>=1.0 && UNIT=="") || UNIT=="K" || UNIT=="k") print sprintf("%.0fK", KB);
else print sprintf("%i ", B);
}
'
}
##################################################
# Archive drivers & helpers
BUILTIN_DRIVER_LIST="DRIVER_TAR DRIVER_TAR_GZ DRIVER_TAR_BZ2 DRIVER_AFIOZ"
DRIVER_TAR ()
{
case $1 in
-test)
require_tools tar
echo "ok"
;;
-suffix)
echo "tar"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar cf $3 -T $4 --numeric-owner --no-recursion 2>&1 \
| grep -v 'tar: Removing leading .* from .* names'
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
tar tf $3 | sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar x --same-permission --same-owner --numeric-owner -f $3 -T $4 2>&1
;;
esac
}
DRIVER_TAR_GZ ()
{
case $1 in
-test)
require_tools tar
echo "ok"
;;
-suffix)
echo "tar.gz"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar czf $3 -T $4 --no-recursion 2>&1 \
| grep -v 'tar: Removing leading .* from .* names'
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
tar tzf $3 | sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar zx --same-permission --same-owner -f $3 -T $4 2>&1
;;
esac
}
DRIVER_TAR_BZ2 ()
{
case $1 in
-test)
require_tools tar bzip2
echo "ok"
;;
-suffix)
echo "tar.bz2"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar cjf $3 -T $4 --no-recursion 2>&1 \
| grep -v 'tar: Removing leading .* from .* names'
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
tar tjf $3 | sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar jx --same-permission --same-owner -f $3 -T $4 2>&1
;;
esac
}
DRIVER_AFIOZ ()
{
case $1 in
-test)
require_tools afio
echo "ok"
;;
-suffix)
echo "afioz"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
afio -Zo $3 < $4 2>&1
# afio -Zo - < $4 > $3 2>&1
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
afio -Zt $3 | sed 's#^#/#' # 's#^#/#;s#\.z$##'
# afio -Zt - < $3 | sed 's#^#/#' # 's#^#/#;s#\.z$##'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
afio -Zinw $4 $3 2>&1
# afio -Zinw $4 - < $3 2>&1
;;
esac
}
# Helpers
require_drivers ()
{
for DRIVER in $@; do
local STATUS=`$DRIVER -test`
if [ "$STATUS" != "ok" ]; then
echo "ERROR: Archive driver not ready: $DRIVER"
echo $STATUS
exit 3
fi
done
}
get_driver_and_file () # Argument: /.
{
local DRIVER
for DRIVER in $USER_DRIVER_LIST $BUILTIN_DRIVER_LIST; do
local SUFFIX=`$DRIVER -suffix`
if [ "$SUFFIX" != "" -a -e "$1.$SUFFIX" ]; then
echo "$DRIVER:$1.$SUFFIX"
break
fi
done
}
get_driver () # Argument:
{
local DRIVER_AND_FILE=`get_driver_and_file $1`
echo ${DRIVER_AND_FILE%%:*}
}
get_archive () # Argument:
{
local DRIVER_AND_FILE=`get_driver_and_file $1`
echo ${DRIVER_AND_FILE##*:}
}
##################################################
# (Un)mount backup disk and (un)lock volume
# Note: The locking mechanism is not completely safe, since in 'mount_dev'
# the existence check and the setting of the lock are seperate operation
# instead of one atomic one (such as "test-and-set"). Atomic operations
# would require support by the underlying file system, the type of which
# we do not know and do not want to dictate. So we consider this little
# synchronization hole tolerable.
MUST_BE_UNMOUNTED=0 # did we mount a device?
VOL_IS_LOCKED=0 # did we already lock (aka "mount") the volume?
mount_dev ()
{
if [ $VOL_IS_LOCKED -eq 1 ]; then return 0; fi # do not lock twice
if [ ! -d $BACKUP_DIR -a "$BACKUP_DEV" != "" ]; then
echo "Mounting $BACKUP_DEV..."
echo
mount $BACKUP_DEV
MUST_BE_UNMOUNTED=1
fi
if [ ! -d $BACKUP_DIR ]; then
echo "ERROR: $BACKUP_DIR not present (mount failed?)"
umount $BACKUP_DEV >& /dev/null
exit 3
fi
# Locking: Begin
if [ -e $BACKUP_DIR/$VOLNAME.lock ]; then
if [ `ps -a | grep -af $BACKUP_DIR/$VOLNAME.lock | grep -a ${0##*/} | wc -l` -gt 0 ]; then
echo "ERROR: Backup volume is locked."
echo
echo "Another instance is currently running. If you are sure that this is not"
echo "the case, then remove the lock file '$BACKUP_DIR/$VOLNAME.lock' manually."
exit 3
fi
fi
echo $$ > $BACKUP_DIR/$VOLNAME.lock
# Locking: End
VOL_IS_LOCKED=1
}
umount_dev ()
{
if [ $VOL_IS_LOCKED -eq 0 ]; then return 0; fi # do not unlock twice
if [[ "`cat $BACKUP_DIR/$VOLNAME.lock 2>/dev/null`" == "$$" ]]; then
# A lock exists and comes from this instance => everything is ok
# Note: This test of a (rare) locking failure is also not perfect
# and may itself fail, e.g. if the backup devices is on a network
# and the lock files are cached locally. However, a (very very rare)
# failure here is tolerable: The user would receive an (obscure)
# error message and can repeat the backup / his operation.
rm -f $BACKUP_DIR/$VOLNAME.lock
if [ $MUST_BE_UNMOUNTED -eq 1 ]; then
echo
echo "Unmounting $BACKUP_DEV..."
umount $BACKUP_DEV
MUST_BE_UNMOUNTED=0
fi
VOL_IS_LOCKED=0
else
# No lock file exists, or it comes from another process => we have a race problem
echo
echo "WARNING: Volume is or was locked by another instance of 'backup2l'."
echo
echo "It appears that another instance of 'backup2l' is or was running concurrently."
echo "This should not happen, but cannot be avoided completely with the present"
echo "implementation of locking in 'backup2l'. I am sorry!"
echo
echo "The following steps should bring everything back into order:"
echo "1. Stop all instances of 'backup2l'."
echo "2. Remove the lock file '$BACKUP_DIR/$VOLNAME.lock' manually."
echo "3. Re-run the backup."
echo "4. Unmount the backup device (if desired)."
fi
}
##################################################
# Purging
purge ()
{
local LV=${#1}
: $[LV-=1]
local LASTDIG=${1:LV:1}
local PREFIX=${1%?}
if [[ $LV == 0 ]]; then
local CAND=$VOLNAME.$1*.list.gz
else
local CAND=$VOLNAME.$PREFIX[$LASTDIG-9]*.list.gz
fi
local ARCH BASE
for ARCH in $CAND; do
BASE=${ARCH%.list.gz}
echo " removing <$BASE>"
rm -fr $BASE.*
done
}
name_cleanup ()
{
local DST=0
local SRC=0
local SUFFIX SRC_FILE DST_FILE CHK
while [[ $SRC -lt 9 ]]; do
: $[SRC+=1]
if [[ -f $VOLNAME.$SRC.list.gz ]]; then
: $[DST+=1]
if [[ "$SRC" != "$DST" ]]; then
for SRC_FILE in $VOLNAME.$SRC* ; do
local SUFFIX=${SRC_FILE##$VOLNAME.$SRC}
local DST_FILE=$VOLNAME.$DST$SUFFIX
if [ ${SUFFIX#*.} == "list.gz" ]; then
echo " moving <${SRC_FILE%.list.gz}> to <${DST_FILE%.list.gz}>"
fi
if [ ${SUFFIX#*.} == "new.gz" -a -h $SRC_FILE ]; then
rm -fr $SRC_FILE
do_symlink ${DST_FILE%.new.gz}.list.gz $DST_FILE
else
mv $SRC_FILE $DST_FILE
fi
done
for CHK in $VOLNAME.$DST*.check ; do
sed "s/ $VOLNAME.$SRC/ $VOLNAME.$DST/" < $CHK > $TMP.check
mv $TMP.check $CHK
done
fi
fi
done
}
do_purge ()
{
if [[ "$1" == "" ]]; then
echo "No archive specified - not purging anything!"
else
mount_dev
cd $BACKUP_DIR
BID_LIST=`expand_bid_list list.gz "$@"`
if [[ "$BID_LIST" == "" ]]; then
echo "No archive(s) matching '$@' found - nothing to purge."
else
echo "Purging <$BID_LIST>..."
for BID in $BID_LIST; do
purge $BID
done
name_cleanup
fi
fi
}
##################################################
# Checking
create_check ()
{
# Parameter: single BID
local BID=$1
if [[ "${#BID}" == "1" ]]; then
local BASE_FILE=""
else
local BASE_FILE=$VOLNAME.`get_base_bid $BID`.list.gz
fi
echo "Creating check file for <$VOLNAME.$BID>..."
rm -f $VOLNAME.$BID.check
md5sum `find . -follow -path "*/$VOLNAME.$BID.*" -type f` $BASE_FILE > $TMP.check
mv $TMP.check $VOLNAME.$BID.check
}
do_create_check ()
{
require_tools $COMMON_TOOLS md5sum
mount_dev
cd $BACKUP_DIR
rm -fr $TMP.*
if [[ "$1" == "" ]]; then
BID_LIST=`expand_bid_list list.gz "*"`
else
BID_LIST=`expand_bid_list list.gz "$@"`
fi
for BID in $BID_LIST; do
if [[ "$1" != "" || ! -f $VOLNAME.$BID.check ]]; then
create_check $BID
fi
done
}
check_arch ()
{
# Parameter: single BID
local BID=$1
echo "Checking archive <$VOLNAME.$BID>..."
# Check the plausibility by file existence...
local SUFFIX
for SUFFIX in list.gz skipped.gz new.gz obsolete.gz error.gz; do
if [[ ! -f $VOLNAME.$BID.$SUFFIX ]]; then
echo " ERROR: File '$VOLNAME.$BID.$SUFFIX' does not exist."
fi
done
if [[ ${#BID} -gt 1 ]]; then
if [[ ! -f $VOLNAME.`get_base_bid $BID`.list.gz ]]; then
echo " ERROR: Base archive <$VOLNAME.`get_base_bid $BID`> does not exist."
fi
fi
# Check check sum file...
if [[ -f $VOLNAME.$BID.check ]]; then
md5sum -c $VOLNAME.$BID.check 2>&1 | sed 's/^/ /'
if [[ $(( `grep -a $VOLNAME.$BID. $VOLNAME.$BID.check | wc -l` )) -lt 6 ]]; then
echo " ERROR: Check file seems to be corrupted."
fi
else
echo " Information: no check file"
fi
}
do_check ()
{
require_tools $COMMON_TOOLS md5sum
mount_dev
cd $BACKUP_DIR
rm -fr $TMP.*
if [[ "$1" == "" ]]; then
BID_LIST=`expand_bid_list list.gz "*"`
else
BID_LIST=`expand_bid_list list.gz "$@"`
fi
for BID in $BID_LIST; do
check_arch $BID
done
}
##################################################
# Print summary
show_summary ()
{
require_tools $COMMON_TOOLS
echo "Summary"
echo "======="
echo
cd $BACKUP_DIR
if [[ `echo $VOLNAME.*.list.gz` == "" ]]; then
echo "No backup archives present."
else
echo "Backup Date Time | Size | Skipped Files+D | New Obs. | Err."
echo "------------------------------------------------------------------------------"
for f in `ls $VOLNAME.*.list.gz` ; do
p=${f%%.list.gz}
size="`du -sbL $p.* | readable_bytes_sum $SIZE_UNITS`"
skipped=$( gunzip -c $p.skipped.gz | wc -l )
total=$( gunzip -c $p.list.gz | wc -l )
new_files=$( gunzip -c $p.new.gz | wc -l )
obsolete=$( gunzip -c $p.obsolete.gz | wc -l )
errors=$( gunzip -c $p.error.gz | grep -a '<' | wc -l )
printf "%-12s %s %s |%8s |%8i %8i |%5i %5i |%5i\n" \
$p $(date -r $p.list.gz +"%Y-%m-%d %H:%M") "$size" \
$skipped $total $new_files $obsolete $errors
done
fi
echo
df -h $BACKUP_DIR
}
##################################################
# backup
compute_level_and_bids ()
{
# Determine level and base BID for new backup...
if [ ! -f $VOLNAME.1.list.gz ]; then
LEVEL="0";
else
if [[ "$1" -gt 0 || "$1" == "0" ]]; then
LEVEL=$1
else
LEVEL=$MAX_LEVEL
fi
BASE_BID=`get_last_bid`
while [[ "$LEVEL" -gt 0 && "${BASE_BID:$LEVEL:1}" -gt "$[MAX_PER_LEVEL-1]" ]]; do
: $[LEVEL-=1]
done
BASE_BID=${BASE_BID:0:$[LEVEL+1]}
BASE_BID=${BASE_BID%%+(0)}
fi
# Determine new archive's name
if [ "$LEVEL" != "0" ]; then
NEW_BID=$BASE_BID
while [ ${#NEW_BID} -le $LEVEL ]; do
NEW_BID=${NEW_BID}"0"
done
: $[NEW_BID+=1]
else
NEW_BID="1"
while [ -f $VOLNAME.$NEW_BID.list.gz ]; do
: $[NEW_BID+=1]
done
fi
}
prepare_backup ()
{
# Input: Comment ("Preparing"/"Estimating"), selected level (optional)
# Output: $LEVEL, $BASE_BID, $NEW_BID
# $TMP.list $TMP.skipped $TMP.new $TMP.obsolete $TMP.files
# Determine level, base BID and new BID
compute_level_and_bids $2
if [ "$LEVEL" != "0" ]; then
echo "$1 differential level-$LEVEL backup <$VOLNAME.$NEW_BID> based on <$VOLNAME.$BASE_BID>..."
else
echo "$1 full backup <$VOLNAME.$NEW_BID>..."
fi
# Determine main list & which files are new or obsolete...
set -f # pathname expansion off as it may destroy $SKIPCOND
OLDIFS="$IFS"
IFS=""
OLDTZ="$TZ"
if [[ "$TIME_ZONE" != "" ]]; then export TZ="$TIME_ZONE"; else unset TZ; fi;
find ${SRCLIST[*]} \( \( ${SKIPCOND[*]} \) \
\( -type d -fprintf $TMP.skipped.dirs "$FORMAT/\n" -o -fprintf $TMP.skipped.files "$FORMAT\n" \) \) \
-o \( -not -type d -printf "$FORMAT\n" -o -printf "$FORMAT/\n" \) \
| sed -e 's# \([0-9]*\..* /.*\)# 00\1#' -e 's# \([0-9]*\..* /\)# 0\1#' \
-e 's#\(\. *\) \([0-9 ]* /\)#\100\2#' -e 's#\. \([0-9 ]* /\)#\.0\1#' \
| sort -k 6 \
> $TMP.list
if [[ "$OLDTZ" != "" ]]; then export TZ="$OLDTZ"; else unset TZ; fi;
IFS=$OLDIFS
cat $TMP.skipped.dirs $TMP.skipped.files \
| sed -e 's# \([0-9]*\..* /.*\)# 00\1#' -e 's# \([0-9]*\..* /\)# 0\1#' \
-e 's#\(\. *\) \([0-9 ]* /\)#\100\2#' -e 's#\. \([0-9 ]* /\)#\.0\1#' \
| sort -k 6 \
> $TMP.skipped
# WORKAROUND: The reason for the two 2-line sed's above is a bug in find 4.1.7, where the format
# directives %04x do not produce leading 0's.
set +f
if [ $LEVEL != 0 ]; then
gunzip -c $VOLNAME.$BASE_BID.list.gz | sort -k 6 | diff - $TMP.list > $TMP.diff
# WORKAROUND: 'sort -k 6' can be removed if sort uses the same options for every user,
# which seems to be not the case!!
if [[ $(( `grep -a "<" $TMP.diff | tail -n 1 | sed 's# /.*##' | wc -w` )) == 5 ]]; then
echo " file '$VOLNAME.$BASE_BID.list.gz' has an old format - using compatibility mode"
sed 's#[0-9]*\.[0-9 ]* /#- /#' < $TMP.list > $TMP.list.old
gunzip -c $VOLNAME.$BASE_BID.list.gz | sort -k 5 | diff - $TMP.list.old > $TMP.diff
fi
grep -a "<" $TMP.diff | sed 's/^< //' > $TMP.obsolete
grep -a ">" $TMP.diff | sed 's/^> //' > $TMP.new
else
# by convention, the *.new and *.obsolete files always exist, although redundant for level-0 backups
do_symlink $TMP.list $TMP.new
touch $TMP.obsolete
fi
eval "$FILTER_NAME" < $TMP.new | grep -av "/$" > $TMP.files # extract real files
# Print statistics...
echo " " `wc -l < $TMP.files` / `grep -av '/$' $TMP.list | wc -l` "file(s)," \
`grep -a '/$' $TMP.new | wc -l` / `grep -a '/$' $TMP.list | wc -l` "dir(s)," \
`grep -av '/$' $TMP.new | readable_bytes_sum`"B /" \
`grep -av '/$' $TMP.list | readable_bytes_sum`"B (uncompressed)"
echo " skipping:" `grep -av '/$' $TMP.skipped | wc -l` "file(s)," \
`grep -a '/$' $TMP.skipped | wc -l` "dir(s)," \
`grep -av '/$' $TMP.skipped | readable_bytes_sum`"B (uncompressed)"
}
show_backup_estimates ()
{
require_tools $COMMON_TOOLS
mount_dev
cd $BACKUP_DIR
rm -fr $TMP.*
prepare_backup "Estimating" $1
rm -f $TMP.*
}
do_backup ()
{
require_tools $COMMON_TOOLS
require_drivers $CREATE_DRIVER
# Print time stamp & mount backup drive...
date
echo
mount_dev
# Run pre-backup
echo "Running pre-backup procedure..."
PRE_BACKUP
# Operate in destination directory
cd $BACKUP_DIR
rm -fr $TMP.*
# Remove old backups...
echo
echo "Removing old backups..."
name_cleanup # should not do anything in normal cases
compute_level_and_bids $1
SAVED_LEVEL=$LEVEL
# Rotate level-0 backups if necessary...
TOO_MANY=$(( ${NEW_BID:0:1} - $MAX_FULL ))
if [[ $TOO_MANY -gt 0 ]]; then
N=0
while [[ $N -lt $TOO_MANY ]]; do
: $[N+=1]
purge $N
done
name_cleanup
compute_level_and_bids $SAVED_LEVEL
fi
# Remove old differential backups...
if [[ $BASE_BID -gt 0 ]]; then
ARCH_LIST=(`ls $VOLNAME.*1.list.gz`)
LV=0
while [ $LV -le 10 ]; do
: $[LV+=1]
MATCHCNT=0
N=${#ARCH_LIST[*]}
while [[ $N -gt 0 ]]; do
: $[N-=1]
BID=`get_bid_of_name ${ARCH_LIST[$N]}`
if [[ ${#BID} == $[LV+1] && ${BID:0:$LV} != ${NEW_BID:0:$LV} ]]; then
: $[MATCHCNT+=1]
if [[ $MATCHCNT -gt $GENERATIONS ]]; then
purge $BID
fi
fi
done
done
fi
# Prepare backup...
echo
prepare_backup "Preparing" $SAVED_LEVEL
# Create and verify archive file...
echo
echo "Creating archive using '"$CREATE_DRIVER"'..."
ARCH_SUFFIX=`$CREATE_DRIVER -suffix`
$CREATE_DRIVER -create $NEW_BID $TMP.$ARCH_SUFFIX $TMP.files 2>&1 | sed 's/^/ /'
if [ "$ARCH_SUFFIX" != "" ]; then
echo "Checking TOC of archive file (< real file, > archive entry)..."
$CREATE_DRIVER -toc $NEW_BID $TMP.$ARCH_SUFFIX | eval "$FILTER_UNIFY_NAME" > $TMP.toc
eval "$FILTER_UNIFY_NAME" < $TMP.files | diff - $TMP.toc | tee $TMP.error | sed 's/^/ /'
fi
# Move files in place...
gzip -9 $TMP.list $TMP.skipped $TMP.obsolete $TMP.error
if [ $LEVEL != 0 ]; then
gzip -9 $TMP.new
else
rm -f $TMP.new
# Here we don't use the do_symlink function because we need to copy or
# symlink different files, depending on wether we copy or symlink
ln -s $VOLNAME.$NEW_BID.list.gz $TMP.new.gz &> /dev/null ||
cp -af $TMP.list.gz $TMP.new.gz
fi
for SUFFIX in skipped.gz new.gz obsolete.gz error.gz $ARCH_SUFFIX list.gz ; do
# *.list.gz has to be the last for transaction safety
mv $TMP.$SUFFIX $VOLNAME.$NEW_BID.$SUFFIX
done
# Create check file if requested...
if [[ "$CREATE_CHECK_FILE" == "1" ]]; then
create_check $NEW_BID
fi
# print summary and finish...
rm -f $TMP.*
# Run post-backup
echo
echo "Running post-backup procedure..."
POST_BACKUP
echo
date
echo
echo
show_summary
}
##################################################
# show_availability
show_availability ()
{
# parameters: masks
require_tools $COMMON_TOOLS
mount_dev 1>&2
cd $BACKUP_DIR
rm -fr $TMP.*
MASK="$@"
if [ "$MASK" = "" ]; then
MASK="/"
fi
echo "Listing available files..." 1>&2
for F in $VOLNAME.*.list.gz; do
BID=`get_bid_of_name $F`
FBID=$BID
while [[ ${#FBID} -lt 5 ]]; do
FBID=$FBID" "
done
for X in $MASK; do
gunzip -c $VOLNAME.$BID.obsolete.gz | grep -a "$X" | sed "s/^/$VOLNAME.$FBID - /"
gunzip -c $VOLNAME.$BID.new.gz | grep -a "$X" | sed "s/^/$VOLNAME.$FBID + /"
done
done
cd /
umount_dev 1>&2
}
##################################################
# show_location
get_location ()
{
# parameter 1: BID of snapshot
# other parameters: masks
if [[ $1 == "head" ]]; then
BID=`get_last_bid`
else
BID=$1
fi
if [[ ! -f $VOLNAME.$BID.list.gz ]]; then
echo "ERROR: Specified backup archive <$VOLNAME.$BID> does not exist!"
exit 3
fi
shift
MASK_LIST="$@"
if [ "$MASK_LIST" = "" ]; then
MASK_LIST="/"
fi
# determine active files...
for MASK in $MASK_LIST; do
gunzip -c $VOLNAME.$BID.list.gz | grep -a "$MASK" | tee $TMP.found | grep -a '/$' >> $TMP.dirs
# dirs go to $TMP.dirs WITH attributes
grep -av '/$' $TMP.found | eval "$FILTER_NAME" >> $TMP.left
# files go to $TMP.left WITHOUT attributes
done
echo "Active files in <$VOLNAME.$BID>:" `wc -l < $TMP.left`
sort < $TMP.left > $TMP.nowleft
mv $TMP.nowleft $TMP.left
# generate location list
LAST_BID="xxx"
touch $TMP.located
touch $TMP.archlist
touch $TMP.noarch
while [ ${#LAST_BID} -gt 1 -a `wc -l < $TMP.left` -gt 0 ]; do
gunzip -c $VOLNAME.$BID.new.gz | eval "$FILTER_NAME" | sort | comm -1 -2 - $TMP.left | tee $TMP.found \
| comm -1 -3 - $TMP.left > $TMP.nowleft
local FOUND=`wc -l < $TMP.found`
printf " found in %-12s%5i (%5i left)\n" \
"$VOLNAME.$BID:" $FOUND `wc -l < $TMP.nowleft`
if [ "$FOUND" -gt 0 ]; then
sed "s/^/$VOLNAME.$BID: /" < $TMP.found >> $TMP.located
mv $TMP.nowleft $TMP.left
DRIVER=`get_driver $VOLNAME.$BID`
if [ "$DRIVER" = "" ]; then
echo $VOLNAME.$BID >> $TMP.noarch
else
echo $VOLNAME.$BID:$DRIVER >> $TMP.archlist
fi
fi
LAST_BID=$BID
BID=`get_base_bid $LAST_BID`
done
echo
# leaves $TMP.left, $TMP.found, $TMP.located, $TMP.dirs, $TMP.archlist, $TMP.noarch
}
show_location ()
{
# parameter 1: BID of snapshot
# other parameters: masks
require_tools $COMMON_TOOLS comm
mount_dev 1>&2
cd $BACKUP_DIR
rm -fr $TMP.*
get_location "$@" 1>&2
echo "Listing locations..." 1>&2
cat $TMP.located
sed 's#^#NOT FOUND: #' < $TMP.left
if [ `wc -l < $TMP.noarch` -gt 0 ]; then
echo -e "\nTo restore, the archive files of the following backups are missing and" \
"\nhave to be copied or linked into $BACKUP_DIR:" 1>&2
sed 's#^# #' < $TMP.noarch 1>&2
else
echo -e "\nAll required archive files are present in $BACKUP_DIR." 1>&2
fi
rm -f $TMP.*
cd /
umount_dev 1>&2
}
##################################################
# do_restore
do_restore ()
{
# parameter 1: . of snapshot
# other parameters: masks
require_tools $COMMON_TOOLS comm
mount_dev
pushd $BACKUP_DIR > /dev/null
rm -fr $TMP.*
# determine which file to get from which archive
get_location "$@"
popd > /dev/null
if [ `wc -l < $BACKUP_DIR/$TMP.noarch` -gt 0 ]; then
echo "Cannot access archive file(s) of the following backup(s):"
sed 's#^# #' < $BACKUP_DIR/$TMP.noarch
echo -e "\nNothing has been restored."
else
# check availability of all required drivers in advance...
require_drivers `sed 's#^.*:##' < $BACKUP_DIR/$TMP.archlist`
# create directories...
DIRS=`wc -l < $BACKUP_DIR/$TMP.dirs`
if [ $DIRS -gt 0 ]; then
echo "Restoring" $DIRS "directories..."
eval "$FILTER_NAME" < $BACKUP_DIR/$TMP.dirs | sed 's#^/\(.*\)$#"\1"#' | xargs -l1 mkdir -p
eval "$FILTER_CHMOD" < $BACKUP_DIR/$TMP.dirs | xargs -l1 chmod
eval "$FILTER_CHOWN" < $BACKUP_DIR/$TMP.dirs | xargs -l1 chown
fi
# process all archives...
echo "Restoring files..."
for ARCH_AND_DRIVER in `cat $BACKUP_DIR/$TMP.archlist`; do
ARCH=${ARCH_AND_DRIVER%%:*}
BID=${ARCH#$VOLNAME.}
DRIVER=${ARCH_AND_DRIVER##*:}
SUFFIX=`$DRIVER -suffix`
grep -a "^$ARCH:" $BACKUP_DIR/$TMP.located \
| sed -e "s#^$ARCH: /##" -e 's#\([][*?]\)#\\\1#g' \
> $BACKUP_DIR/$TMP.curlist
# The second sed expression escapes special glob(7) characters ([]*?).
FILES=`wc -l < $BACKUP_DIR/$TMP.curlist`
echo " $ARCH.$SUFFIX:" $FILES "file(s) using '"$DRIVER"'"
$DRIVER -extract $BID $BACKUP_DIR/$ARCH.$SUFFIX $BACKUP_DIR/$TMP.curlist | sed "s/^/ /"
done
fi
# Cleanup...
rm -f $BACKUP_DIR/$TMP.*
}
##################################################
# External archiving
do_external ()
{
require_tools $COMMON_TOOLS split tail md5sum
CTRL_EXPR="$VOLNAME\.[0-9]*\.((list)|(new)|(obsolete)|(skipped)|(error)|(check))"
mount_dev
CAPACITY=$(( $1 * 1024 ))
shift
MAXFREE=$(( $1 * 1024 ))
shift
# determine BID_LIST...
BID_LIST=`expand_bid_list list.gz "$@"`
# determine distribution...
LAST_DISK=1
REMAINING=$CAPACITY
rm -f TMP.xdist 2> /dev/null
touch TMP.xdist
for BID in $BID_LIST; do
CTRL_SIZE=`ls -1s $BACKUP_DIR/$VOLNAME.$BID.* | grep -E "$CTRL_EXPR" \
| awk '{ sum += $1 } END { print sum }'`
if [ $CTRL_SIZE -gt $REMAINING -a $REMAINING -lt $CAPACITY ]; then
: $[ LAST_DISK += 1 ]
REMAINING=$CAPACITY
fi
LAST_DISK_DIR=`printf "data-%02d" $LAST_DISK`
ls -1s $BACKUP_DIR/$VOLNAME.$BID.* | grep -E "$CTRL_EXPR" \
| sed "s#$BACKUP_DIR#$LAST_DISK_DIR#" >> TMP.xdist
: $[ REMAINING -= $CTRL_SIZE ]
for ARCHIVE_PATH in `ls -1d $BACKUP_DIR/$VOLNAME.$BID.* | grep -vE "$CTRL_EXPR"`; do
ARCHIVE=${ARCHIVE_PATH##*/}
if [ -f $BACKUP_DIR/$ARCHIVE ]; then
DATA_SIZE=`ls -1s $BACKUP_DIR/$ARCHIVE | sed 's/ [^ ]*$//'`
if [ $DATA_SIZE -gt $REMAINING -a $REMAINING -lt $MAXFREE ]; then
: $[ LAST_DISK += 1 ]
REMAINING=$CAPACITY
fi
if [ $DATA_SIZE -le $REMAINING ]; then
LAST_DISK_DIR=`printf "data-%02i" $LAST_DISK`
ls -1s $BACKUP_DIR/$ARCHIVE | sed "s#$BACKUP_DIR#$LAST_DISK_DIR#" >> TMP.xdist
: $[ REMAINING -= $DATA_SIZE ]
else
SPLIT_NO=1
while [ $DATA_SIZE -gt $REMAINING ]; do
LAST_DISK_DIR=`printf "data-%02i" $LAST_DISK`
printf "%4i $LAST_DISK_DIR/$ARCHIVE.%02i\n" $REMAINING $SPLIT_NO >> TMP.xdist
: $[ DATA_SIZE -= $REMAINING ]
: $[ SPLIT_NO += 1 ]
: $[ LAST_DISK += 1 ]
REMAINING=$CAPACITY
done
LAST_DISK_DIR=`printf "data-%02i" $LAST_DISK`
printf "%4i $LAST_DISK_DIR/$ARCHIVE.%02i\n" $DATA_SIZE $SPLIT_NO >> TMP.xdist
: $[ REMAINING -= $DATA_SIZE ]
fi
else
echo "WARNING: Cannot handle directory: $ARCHIVE (skipping)"
echo
fi
done
done
SPACE=$(( (`grep -a "\.[0-9]*$" TMP.xdist | awk '{ sum += $1 } END { print sum }'` + 1023) / 1024 ))
cat << EOT
I am about to split and combine the selected backup archives into directories
of equal size, so that they can be stored on a set of removable media
(e. g. CDs). If 'cdlabelgen' is installed, I will create CD covers.
It is up to you to burn the CDs or store the data in whichever way you like.
All files are generated in the current working directory ($PWD).
Make sure it is empty!
In order to save disk space, only symbolic links will be generated wherever
possible. Make sure that they are followed by your CD-burn/storage tool and
that the backup device is mounted!
You have selected a medium capacity of $(( $CAPACITY / 1024 )) MB with a maximum waste of $(( $MAXFREE /1024 )) MB
per medium. The selected BIDs are:
$BID_LIST
I need about $SPACE MB of disk space in the current directory.
You will get $LAST_DISK volume(s).
EOT
read -p "Do you want to see details? [y/N] " ANSWER
if [[ "$ANSWER" == "y" ]]; then
echo -e "\nSize Volume/File\n======================================="
cat TMP.xdist
fi
echo
read -p "Do you want to continue? [y/N] " ANSWER
if [[ "$ANSWER" != "y" ]]; then
rm -f TMP.xdist
return
fi
echo -e "\nCreating links..."
rm -fr data-??
# create directories...
sed -e 's#^[ 0-9]*##' -e 's#/.*$##' < TMP.xdist | sort -u | xargs -l1 mkdir -p
# create links...
for FILE in `grep -av '\.[0-9]*$' TMP.xdist | sed 's#^[ 0-9]*##'`; do
do_symlink $BACKUP_DIR/${FILE#data-??/} $FILE
done
echo "Splitting large files..."
for FILE in `grep -a '\.01$' TMP.xdist | sed -e 's#^[ 0-9]*##' -e 's#.01$##'`; do
ORG_FILE=${FILE#data-??/}
echo " $ORG_FILE"
HEAD_SIZE=$(( `grep -a $FILE TMP.xdist | sed 's/ [^ ]*$//'` * 1024 ))
head $BACKUP_DIR/$ORG_FILE -c $HEAD_SIZE > $FILE.01
tail $BACKUP_DIR/$ORG_FILE -c +$(( $HEAD_SIZE + 1 )) | split -b $(( $CAPACITY * 1024 )) - TMP.split.
SPLIT_NO=2
for SPLIT in `ls -1 TMP.split.*`; do
DST_FILE=`printf "$ORG_FILE.%02i" $SPLIT_NO`
DST=`grep -a $DST_FILE TMP.xdist | sed 's#^[ 0-9]*##'`
mv $SPLIT $DST
: $[ SPLIT_NO += 1 ];
done
done
# create self-check files...
cat << EOT
I can now generate check scripts for each volume that can later be used to
verify the integrity of all files. This is e.g. useful if, in a couple of
years, you want to know whether your backup media are still readable.
Then simply mount your media and type '. check_these_files.sh' inside the
media's main directory.
EOT
read -p "Create self-check scripts? [Y/n] " ANSWER
if [[ "$ANSWER" != "n" ]]; then
echo
echo "Creating self-check scripts..."
for DISK in data-??; do
echo " $DISK"
cd $DISK
DST_FILE="check_these_files.sh"
rm -f $DST_FILE
FILES=`find . -follow -type f`
echo "#!/bin/sh" > $DST_FILE
echo >> $DST_FILE
echo "echo \"This script has been auto-generated by backup2l v$VER.\"" >> $DST_FILE
echo "echo \"Verifying file(s) using md5sum(1)...\"" >> $DST_FILE
echo >> $DST_FILE
echo "md5sum -v -c << EOF" >> $DST_FILE
md5sum $FILES >> $DST_FILE
echo "EOF" >> $DST_FILE
cd ..
done
fi
# create CD labels
if which cdlabelgen > /dev/null; then
echo
echo "Creating CD labels..."
read -p " Enter CD title [Backup]: " ANSWER
if [[ "$ANSWER" == "" ]]; then
ANSWER="Backup"
fi
if [ -r /usr/share/cdlabelgen/penguin.eps ]; then
TRAYPIC="-e /usr/share/cdlabelgen/penguin.eps -S 0.5"
else
TRAYPIC=""
fi
for DISK in data-??; do
DISK_NO=${DISK#data-}
echo -e "\nContents\n========\n" > contents-$DISK_NO.txt
grep -a $DISK TMP.xdist | sed -e 's#^.*/##' -e 's#\.list\.gz# - control files#' \
| grep -vE "$CTRL_EXPR" >> contents-$DISK_NO.txt
cdlabelgen -c "$ANSWER" -s "${DISK_NO#0} of $LAST_DISK" -d `date -I` \
-f contents-$DISK_NO.txt $TRAYPIC -o cd-cover-$DISK_NO.ps
done
fi
# clean up
rm -f TMP.xdist
}
##################################################
# Usage & banner
banner ()
{
echo backup2l v$VER by Gundolf Kiefer
echo
}
usage ()
{
banner
cat << EOF
Usage: backup2l [-c ] [-t ]
Where
-c | --conf : specifies configuration file [/etc/backup2l.conf]
-t | --time : specifies backup ID as a point-in-time for --locate and --restore
:
-h | --help : Help
-b | --backup [] : Create new backup
-e | --estimate [] : Like -b, but nothing is really done
-s | --get-summary : Show backup summary
-a | --get-available : Show all files in all backups containing in their path names
-l | --locate [] : Show most recent backup location of all active files matching
-r | --restore [] : Restore active files matching into current directory
-p | --purge : Remove the specified backup archive(s) and all depending backups
-v | --verify [] : Verify the specified / all backup archive(s)
-m | --make-check [] : Create md5 checksum file for the specified archive(s) / wherever missing
-x | --extract :
Split and collect files to be stored on removable media (e. g. CDs)
EOF
echo "Built-in archive drivers:" $BUILTIN_DRIVER_LIST
if [ "$USER_DRIVER_LIST" != "" ]; then
echo "User-defined drivers: " $USER_DRIVER_LIST
fi
}
##################################################
# Main
shopt -s nullglob
shopt -s extglob
unset LANG LC_ALL LC_COLLATE # otherwise: unpredictable sort order in sort, ls
# Set defaults...
CREATE_DRIVER="DRIVER_TAR_GZ" # works with last stable version 1.01
# Read & validate setup...
CONF_FILE="/etc/backup2l.conf"
if [ "$1" = "-c" -o "$1" = "--conf" ]; then
shift
CONF_FILE=$1
shift
fi
if [ -f "$CONF_FILE" ]; then
. $CONF_FILE
else
echo "Could not open configuration file '$CONF_FILE'. Aborting."
exit 3
fi
if [ "$UNCONFIGURED" = "1" ]; then
banner
echo -e "The configuration file '$CONF_FILE' has to be edited before using ${0##*/}.\n"
echo -e "For help, look into the comments or read the man page.\n"
exit 3
fi
if [ "$VOLNAME" = "" -o "$SRCLIST" = "" -o "${SKIPCOND[*]}" = "" -o "$BACKUP_DIR" = "" -o \
"$MAX_LEVEL" = "" -o "$MAX_PER_LEVEL" = "" -o "$MAX_FULL" = "" ]; then
echo "ERROR: The configuration file '$CONF_FILE' is missing or incomplete."
exit 3
fi
if [ "$FOR_VERSION" = "" ]; then
FOR_VERSION="0.9"
fi
if [[ "$FOR_VERSION" < "1.1" || "$FOR_VERSION" > "${VER%-*}" ]]; then
banner
cat << EOF
The configuration file '$CONF_FILE' seems to be written for
version $FOR_VERSION and may be incompatible with this version of backup2l.
The following variables have been added, removed or their syntax may have
changed. Details can be found in first_time.conf and the man page.
1.1 : CREATE_DRIVER, USER_DRIVER_LIST
0.93: POST_BACKUP
0.91: SRCLIST, SKIPCOND, FOR_VERSION
If you think your configuration file is correct, please change the value
of FOR_VERSION.
EOF
exit 3
fi
TMP="TMP.$VOLNAME"
# Read time point if given
if [ "$1" = "-t" -o "$1" = "--time" ]; then
shift
BID=${1#$VOLNAME.}
shift
else
BID="head"
fi
# Go ahead...
case $1 in
-h | --help)
usage
;;
-e | --estimate)
banner
show_backup_estimates "$2"
;;
-b | --backup)
banner
do_backup "$2"
;;
-s | --get-summary)
banner
mount_dev
show_summary
;;
-a | --get-available)
banner 1>&2
shift
show_availability "$@"
;;
-l | --locate)
banner 1>&2
shift
show_location $BID "$@"
;;
-r | --restore)
banner
shift
do_restore $BID "$@"
;;
-p | --purge)
banner
shift
do_purge "$@"
;;
-v | --verify)
banner
shift
do_check "$@"
;;
-m | --make-check)
banner
shift
do_create_check "$@"
;;
-x | --extract)
banner
shift
if [[ "$3" == "" ]]; then
usage
else
do_external "$@"
fi
;;
*)
if [ "$AUTORUN" = "1" ]; then
banner
do_backup
else
usage
fi
;;
esac
# Unmount backup device if it was mounted
cd / # avoid "device is busy" during unmount
umount_dev
backup2l-1.6/backup2l.8 0000664 0000000 0000000 00000037115 12706673016 0014676 0 ustar 00root root 0000000 0000000 .TH "BACKUP2L" "8" "August 17, 2010" "backup2l v1.6"
.SH NAME
backup2l \- low\-maintenance backup/restore tool
.SH SYNOPSIS
.B backup2l
[
.BI "\-c " conffile
] [
.BI "\-t " "backup\-ID"
]
.I command
.SH DESCRIPTION
\fBbackup2l\fP is a tool for generating, maintaining and restoring backups on a mountable file system
(e. g. hard disk). The main design goals are low maintenance effort, efficiency, transparency and robustness.
It features differential backups at multiple hierarchical levels and provides rollback functionality.
All control files are stored together with the archives on the backup device, and their contents are
mostly self\-explaining. Hence, in the case of an emergency, a user does not only have to rely on the
restore functionality of \fBbackup2l\fP, but can \- if necessary \- browse the files and extract
archives manually.
An open driver architecture allows to use virtually any archiving program as a backend.
Built\-in drivers support \fB.tar.gz\fP, \fB.tar.bz2\fP, or \fB.afioz\fP files. Further drivers can be
added by the user. When restoring data, an appropriate driver is selected automatically for each
archive depending on the suffix of the archive file.
The method of hierarchical differential backups is a generalization to the concept of the "daily",
"weekly" and "monthly" backups.
Each backup has a level and a serial number. Maximum\-level backups are comparable with
daily differential backups, level\-0 backups are full backups. For example, let \fBMAX_LEVEL\fP be 4
and \fBMAX_PER_LEVEL\fP be 5. After 5 level\-4 backups (e. g. after 5 days), a new level\-3 backup is made.
After 5 level\-3 backups (and 5*5 at level\-4), a new level\-2 backup is made, and so on.
Each differential backup contains the changes towards the previous backup of the same or a lower level.
This scheme allows to efficiently generate small incremental backups at short intervals.
At the same time, the total number of archives that have to be stored (or processed in the case of a restore)
only increases logarithmically with the number of backups since the last full backup. Time\-consuming
full backups are only sparsely needed.
In the example above, a new full backup is only necessary after 780 (=5^4+5^3+5*5+5) days, while only
at most 20 (=4*5) archives have to be processed.
For \fBbackup2l\fP, each backup archive is identified by its \fBbackup ID (BID)\fP. The number of digits
determines the level. Level\-0 (full) backups have a 1\-digit BID, level\-n backups have a BID of n+1 digits.
The last digit is a serial number, the prefix identifies the lower\-level backups on which a given backup
is based on. For example, the archive 235 contains the differences towards archive 234, and to restore
the file system state of the time it was generated, the full backup 2, the level\-1 backups 21, 22, 23
and the level\-2 backups 231, ..., 235 have to be processed.
All serial numbers are between 1 and 9, a zero in the BID indicates that no archive of the respective level
is contained in the chain. For example, the level\-3 backup 1201 is immediately based on the level\-1
backup 12.
For deciding whether a file is new or modified, \fBbackup2l\fP looks at its name, modification time, size,
ownership and permissions. Unlike other backup tools, the i\-node is not considered in order to avoid problems with
non\-Unix file systems like FAT32.
.SH OPTIONS
.TP
.BI "\-c, \-\-conf " conffile
This argument specifies the configuration file (default: \fBetc/backup2l.conf\fP).
.TP
.BI "\-t, \-\-time " "BID"
If present, this option selects a certain backup for the \fB\-\-locate\fP and \fB\-\-restore\fP commands.
E. g., the latter will restore files and directories exactly as they were on the system at the time when the
specified backup was made. If not present, the latest available backup is selected.
.SH COMMANDS
.TP
.BI "\-h, \-\-help"
Display the usage info.
.TP
.BI "\-e, \-\-estimate " "\fR[\fP level \fR]\fP"
Prints the number of files, estimated amount of data and other information on the backup that would be
generated next. No backup archives are actually created
or removed. If specified, the parameter \fBlevel\fP overrides the \fBMAX_LEVEL\fP setting.
.TP
.BI "\-b, \-\-backup " "\fR[\fP level \fR]\fP"
Creates a new backup and removes old archives based on the given configuration file.
If specified, the parameter \fBlevel\fP overrides the \fBMAX_LEVEL\fP setting. This is useful e. g. shortly
before or after major changes are performed with the file system. In this case, a lower level should be
specified in order to avoid that a large number of files are backed up multiple times again.
.TP
.BI "\-s, \-\-get\-summary"
Shows a table describing each backup (date, size, files, ...) and the file system usage of the backup device.
.TP
.BI "\-a, \-\-get\-available " "\fR[\fP pattern list \fR]\fP"
Shows all files removed and added for all backups. A '+' in the output indicates that the file is new and
thus contained in the archive file. A '\-' indicates that the file has been removed (or replaced).
If one or several patterns are supplied, \fBgrep(1)\fP is used to filter
the list. All status messages go to \fBstderr\fP, so that the generated file list can easily be redirected.
\fBNote:\fP The search pattern is not just applied to the file names, but to the whole entry in the \fB.list.gz\fP
file. This allows you to not only search for file names but also for other attributes like
ownership, modification time etc. . In order to apply a search pattern to file names only, precede it by
"/.*".
.TP
.BI "\-l, \-\-locate " "\fR[\fP pattern list \fR]\fP"
Shows most recent backup location for active files. If one or several patterns are supplied, \fBgrep(1)\fP
is used to filter the list in the same way as for \fB\-\-get\-available\fP (see above) . All status messages
go to \fBstderr\fP, so that the generated file list can easily be redirected.
Active files are files that have been on the system at the time of the selected backup, which is either
the latest backup or the one specified by \fB\-\-time\fP (see above). Files that were removed at that time
but are still stored in some later archive will not be shown.
Altogether, this command tells you, which files have to be extracted from which archive in order to
restore the state of the system at the time of the selected backup.
.TP
.BI "\-r, \-\-restore " "\fR[\fP pattern list \fR]\fP"
Performs the same steps like \fB\-\-locate\fP and then restores the respective files. All files are restored
relative to the current directory. They can be restored to their original location by cd'ing into / before,
but this is not recommended.
.TP
.BI "\-p, \-\-purge " "BID list"
Removes the specified backup archive(s) and all depending backups.
.TP
.BI "\-m, \-\-make\-check " "\fR[\fP BID list \fR]\fP"
Creates (a) check file(s) for the specified archive(s) using \fBmd5sum(1)\fP.
If no BID is specified, check files are created wherever missing.
.TP
.BI "\-v, \-\-verify " "\fR[\fP BID list \fR]\fP"
Verifies the specified backup archive(s). If no BID is specified, all existing archives are checked.
If a check file exists, this allows a comprehensive test including e. g. media failures. If the check file
is missing, only the existence of all files and the immediate base archive are verified.
.TP
.BI "\-x, \-\-extract " "capacity max\-free BID\-list"
Split and collect files to be stored on removable media (e. g. CDs). \fBcapacity\fP is the medium
capacity in MB. \fBmax\-free\fP is the maximum amount of empty space on each medium (except for the last
one, of course). \fBBID\-list\fP specifies the archives and may contain wildcards, e. g.: 1 '2*'.
The operation generates enumbered subdirectories representing the media contents.
Some more files are generated that may be useful, e. g. to print labels.
While guaranteeing a minimum waste of \fBmax\-free\fP MB per medium, the collection procedure preserves
the ordering of files and keeps all control files of an archive always together on the same medium.
Large archive files are split into multiple files with serial numbers appended to their names.
The operation is interactive. Just run it and look what it is about to do.
If that is not what you want, you can stop it.
.SH CONFIGURATION
In the configuration file (\fB/etc/backup2l.conf\fP by default), the following variables have to be set,
following the \fBbash(1)\fP syntax:
.TP
.BI "FOR_VERSION=" "version"
Defines the \fBbackup2l\fP version for which the configuration file is written. This way,
future versions can automatically print a warning if the syntax has changed.
.TP
.BI "SRCLIST=(" " source list " ")"
This is a blank\-separated list of all top\-level directories to make backups of.
Directory names with spaces have to be quoted, e. g.: SRCLIST=("/my dir" /another/dir).
The last elements of the list may be options for \fBfind(1)\fP, for example \fB\-xdev\fP in
order to skip subdirs on other file systems like /dev or /proc.
.TP
.BI "SKIPCOND=(" " find condition " ")"
Files for which this condition is 'true' are not considered for backup.
See \fBfind(1)\fP for information on how to formulate possible conditions.
Special characters ("(", ")", "!", ...) must be quoted by a leading backslash
("\\(", "\\)", " \\!", ...). An empty condition (i. e. if you do not want
any files to be skipped) must be specified as "( \-false )".
.TP
.BI "[ BACKUP_DEV=""" "mount_point" """ ]"
If defined, \fBbackup2l\fP mounts the backup device before any operation.
Afterwards, it is unmounted unless it was already mounted before.
.TP
.BI "BACKUP_DIR=""" "backup dir" """"
Destination directory for backup files. This must be different from \fBMOUNT_POINT\fP, i. e.
a subdirectory on the device.
.TP
.BI "VOLNAME=""" "volname" """"
This is a common prefix for all backup and control files. Multiple backup volumes are possible
if for each volume a separate configuration file is written.
.TP
.BI "MAX_LEVEL=" "max_level"
Maximum backup level. Possible values are 1..9.
.TP
.BI "MAX_PER_LEVEL=" "max_per_level"
Number of differential backups per level. Possible values are 1..9.
.TP
.BI "MAX_FULL=" "max_full"
Number of full backups kept. Possible values are 1..8.
.TP
.BI "GENERATIONS=" "generations"
Number of backup generations to keep for each non\-zero level. Old backups are automatically removed
as long as at least \fBGENERATIONS * MAX_PER_LEVEL\fP backups for the respective level remain.
For example, with \fBMAX_LEVEL=3, MAX_PER_LEVEL=5, GENERATIONS=2\fP it is always possible to access
the last 10 level\-3 (e. g. daily) backups, the last 10 level\-2 backups (e. g. 5, 10, 15, ..., 50 days old),
and so on.
.TP
.BI "PRE_BACKUP () { " "do something" " }"
This function is called before writing the backup. It can be used to dump some important system information,
e. g. the HD's partition table, to a file which is then backed up.
.TP
.BI "POST_BACKUP () { " "do something" " }"
This function is called after writing the backup. Together with \fBPRE_BACKUP\fP it can be used to stop and restart
e. g. database or mail services which may frequently alter some files that have to be backed up.
.TP
.BI "[ AUTORUN=1 ]"
If set to 1, \fBbackup2l\fP performs the \fB\-\-backup\fP operation when invoked without arguments.
Otherwise, the usage information is shown.
.TP
.BI "[ SIZE_UNITS= " "B | K | M | G" " ]"
Sets the units for archive sizes in summary listings to bytes, KB, MB, or GB.
If unset, a user\-readable format is chosen automatically. If set, the units are the same for the whole table,
which may be even more user\-friendly.
.TP
.BI "[ CREATE_DRIVER=""" "archive driver" """ ]"
Selects an archive driver for creating backups. An archive driver is responsible for managing backup
files. If unset, the default driver "DRIVER_TAR_GZ" is used. The \fB\-\-help\fP operation lists all
available drivers. More drivers can be defined in the configuration file (see below).
.TP
.BI "[ USER_DRIVER_LIST=""" "user\-defined drivers" """ ]"
Declares additional, user\-defined archive drivers which are implemented in the configuration file.
The sample configuration file contains a commented example. Read it in order to learn how to
implement your own driver.
.SH FILES
.TP
.B /etc/backup2l.conf
Configuration file.
.TP
.IB "VOLNAME" "." "BID" ".tar.gz, " "VOLNAME" "." "BID" ".afioz, ..."
Archive files.
.TP
.IB "VOLNAME" "." "BID" ".list.gz"
List of all active files when the backup was made. Each file is preceded with its size,
modification time, and other information.
.TP
.IB "VOLNAME" "." "BID" ".new.gz"
List of all new ore modified files when the backup was made (pathnames only). Unless an error
occured, this list reflects the contents of the archive.
.TP
.IB "VOLNAME" "." "BID" ".obsolete.gz"
List of all obsolete files when the backup was made (pathnames only).
.TP
.IB "VOLNAME" "." "BID" ".skipped.gz"
Complete list of all files that were skipped according to \fBSKIPMASK\fP.
.TP
.IB "VOLNAME" "." "BID" ".error.gz"
This file is generated by comparing the \fB.new.gz\fP file with the actual archive
contents using \fBdiff(1)\fP. If the error file is non\-empty, something may have gone wrong.
.TP
.IB "VOLNAME" "." "BID" ".check"
MD5 check sums of all files of the present archive and the \fB.list.gz\fP file of the base archive.
This file is optional and may be used by the \fB\-\-verify\fP operation.
.SH INVOCATION BY CRON
\fBbackup2l\fP is designed to be run autonomously as a cron job.
If the variable \fBAUTORUN\fP is set, it generates a backup if invoked without any parameters, and
you can simply create a symlink, e. g. by:
.ce
ln \-s `which backup2l` /etc/cron.daily/zz\-backup2l
The "zz\-" prefix causes the backup job to be the last one executed, so that other jobs are not delayed
if the backup takes somewhat longer. The status output is e\-mailed to root by the cron daemon.
.SH MANIPULATING FILES AND CONFIGURATIONS
\fBbackup2l\fP has been designed to be robust with respect to errors and configuration changes.
If the backup process is interrupted, e. g. because of a shutdown while it is running,
no serious data corruption can occur. Some temporary files may remain which are cleaned up during the
next run. If file is changed during the backup generation, it may not be contained in the current
backup. However, it is guaranteed that it is considered modified during the next backup.
In order to save disk space, e. g. after some archives have been copied to external media,
archive files (.tar.gz or .afioz, for example) can safely be removed from the backup directory.
As long as all control files are kept, \fBbackup2l\fP retains full functionality as far as possible.
The \fB\-\-restore\fP command prompts for eventually missing archive files for the respective request
(and only those). The \fB\-\-extract\fP command completely ignores all backups with missing archive files.
The configuration, especially the settings for \fBMAX_LEVEL\fP, \fBMAX_PER_LEVEL\fP,
\fBMAX_FULL\fP and the specification of source files,
can be arbitrarily changed without having to expect data corruption. \fBbackup2l\fP will
gracefully adapt the new settings during the next run.
.SH BUGS
After a restore operation, the modification time of directories is equal
to the restoration time while for files it is equal to the original modification
time.
.SH AUTHOR
\fBbackup2l\fP was written by Gundolf Kiefer .
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 2 of the License,
or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.SH SEE ALSO
.BR "tar(1), afio(1), find(1), grep(1), md5sum(1)"
backup2l-1.6/debian/ 0000775 0000000 0000000 00000000000 12706673016 0014315 5 ustar 00root root 0000000 0000000 backup2l-1.6/debian/changelog 0000664 0000000 0000000 00000012001 12706673016 0016161 0 ustar 00root root 0000000 0000000 backup2l (1.6) unstable; urgency=low
* A detailed upstream changelog can be found at
https://github.com/gkiefer/backup2l.
* Improve locking, partially based on the patch "conditional unlocking" by
Joachim Wiedorn
* Apply patch "fix for grep (>2.20) with encoded filenames" by Joachim Wiedorn
* Apply patch "rm tmp before post backup" by Joachim Wiedorn
* make .list.gz files independent from timezone by adding a new TIME_ZONE
parameter (closes: #581344)
* optimized man page to comply with Debian rules (patch by Joachim
Wiedorn, May 8 2010)
* added some more example drivers donated by various users
-- Gundolf Kiefer Sat, 23 Apr 2016 14:00:44 +0200
backup2l (1.5) unstable; urgency=low
* Add self-test script to each external volume
* changed FORMAT to truncate fractional parts from time stamps in .list.gz files (needed for Debian 5.0/Lenny)
* backup2l.conf: added some comments on how to use SKIPCOND for certain features
* fixed false errors with special characters, closes #291423, #305792
* changed chown invocation ("g:u" instead of "g.u")
-- Gundolf Kiefer Tue, 13 Dec 2009 20:13:05 +0100
backup2l (1.4) unstable; urgency=low
* Misc. adaptions to allow directory structures as archives
* first-time.conf: added section with donated sample drivers
* DRIVER_AFIOZ: accesses archives only via stdin/stdout to allow files > 2GB
* Improved POSIX compliance, closes #261950
* Improved support for complex SKIPCOND settings including "(", ")", "!"
* Fixed restore of files with glob(7) control characters, closes #235249
* misc. typos in doc, closes #250672, #250671
* adopted documentation to correctly state that MAX_FULL must be <= 8, closes #233962
* some adaptions to Mac OS X with the help of Joe Auricchio
* turned SED_* macros into FILTER_* to allow alternative filter program instead of sed
* install-sh: autodetects cron/daily directory (compatibility with Mac OS X)
* install-sh: fixed error in usage ()
-- Gundolf Kiefer Tue, 4 Jan 2005 16:10:05 +0100
backup2l (1.3) unstable; urgency=low
* Removed "--same-order" option in tar-based drivers (fixes problems during restore)
* Improved checking of required drivers before starting a restore operation
* Fixed some typos in comments and documentation
* Set Debian standards version to 3.6.1
-- Gundolf Kiefer Wed, 8 Oct 2003 19:22:00 +0200
backup2l (1.2) unstable; urgency=low
* Fixed false alarms with special characters in file names (closes #763710, #587658)
* Added DRIVER_TAR (Philippe "BooK" Bruhat)
* Moved Debian adaptions by Chris Davis upstream
* Set Debian standards version to 3.6.0
-- Gundolf Kiefer Mon, 2 Sep 2003 20:47:17 +0200
backup2l (1.1-gk.1.1) unstable; urgency=low
* Initial upload into Debian (closes: 142857)
* Remove unnecessary postinst
-- Chris G. Davis Fri, 25 Jul 2003 15:10:34 -0400
backup2l (1.1-gk.1) unstable; urgency=low
* New: open driver architecture for different archive formats
* fixed bugs with regexps for --restore, --locate, --get-available
-- Gundolf Kiefer Sun, 18 May 2003 12:16:07 +0200
backup2l (1.01-gk.1) unstable; urgency=low
* fixed incorrect backup list sorting (bug #637066)
* removed "bc" usage
* stricter filter macros for SED_NAME, SED_CHOWN, SED_CHMOD (should fix various bugs including patch 702979 from Gene Skonicki)
* human readable sizes (based on patch by Jarno Elonen)
* allow wildcards in argument of ANY operation
* support for filesystems not allowing symlinks (patch by Jason Creighton)
-- Gundolf Kiefer Sun, 9 Apr 2003 12:59:00 +0200
backup2l (1.00-gk.1) unstable; urgency=low
* Fixed problem with special characters in file names (bug #587658)
* .tar.gz files can savely be removed
* Various fixes/patches
-- Gundolf Kiefer Sun, 8 Sep 2002 17:30:00 +0200
backup2l (0.93-gk.1) unstable; urgency=low
* Changed: diff is invoked without '-d' (performance issue with large numbers of files)
* New: POST_BACKUP
* Fixed: compatibility with pre-0.9 .list files
* Various fixes/patches
-- Gundolf Kiefer Thu, 9 Mar 2002 15:26:00 +0100
backup2l (0.92-gk.1) unstable; urgency=low
* Fixed: incorrectly determined base archive resulting in full backup instead of incremental
-- Gundolf Kiefer Thu, 17 Jan 2002 11:11:01 +0100
backup2l (0.91-gk.1) unstable; urgency=low
* New: command to generate CD-ready data directories
* Time in summary display
* Workaround for bug in find 4.1.7 (no leading 0's in printf if requested)
* Use of shell arrays for SRCLIST
* Fixed problems with spaces in file names
* Minor bugfixes
-- Gundolf Kiefer Tue, 6 Nov 2001 09:32:13 +0100
backup2l (0.9-gk.1) unstable; urgency=low
* Initial release
-- Gundolf Kiefer Mon, 29 Oct 2001 14:58:47 +0100
Local variables:
mode: debian-changelog
End:
backup2l-1.6/debian/conffiles 0000664 0000000 0000000 00000000057 12706673016 0016212 0 ustar 00root root 0000000 0000000 /etc/backup2l.conf
/etc/cron.daily/zz-backup2l
backup2l-1.6/debian/control 0000664 0000000 0000000 00000003002 12706673016 0015713 0 ustar 00root root 0000000 0000000 Source: backup2l
Section: admin
Priority: optional
Maintainer: Gundolf Kiefer
Standards-Version: 3.9.6
Package: backup2l
Architecture: all
Depends: findutils (>= 2.1)
Recommends: tar (>= 1.13.17) | afio (>= 2.4.7)
Suggests: bzip2 (>= 1.0.2), cdlabelgen
Description: low-maintenance backup/restore tool for mountable media
backup2l [backup-too-l] is a tool for autonomously generating, maintaining
and restoring backups on a mountable file system (e. g. hard disk). In a
default installation, backups are created regularly by a cron script.
.
The main design goals are low maintenance effort, efficiency, transparency
and robustness. All control files are stored together with the archives on
the backup device, and their contents are mostly self-explaining. Hence, a
user can - if necessary - browse the files and extract archives manually.
.
backup2l features differential backups at multiple hierarchical levels.
This enables the efficient generation of small incremental backups at
short intervals while at the same time, the total number of archives only
increases logarithmically with the number of backups since the last full
backup.
.
An open driver architecture enables the use of virtually any archiving
program as a backend. Built-in drivers support .tar.gz, .tar.bz2, or .afioz
files. Further user-defined drivers can be added.
.
An integrated split-and-collect function allows one to comfortably transfer
all or selected archives to a set of CDs or other removable media.
backup2l-1.6/debian/copyright 0000664 0000000 0000000 00000001566 12706673016 0016260 0 ustar 00root root 0000000 0000000 Copyright (c) 2001-2016 by Gundolf Kiefer
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
On Debian systems, the complete text of the GNU General Public
License can be found in /usr/share/common-licenses/GPL-2 file.
backup2l-1.6/debian/prerm 0000664 0000000 0000000 00000000176 12706673016 0015371 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
if [ \( "$1" = "upgrade" -o "$1" = "remove" \) -a -L /usr/doc/backup2l ]; then
rm -f /usr/doc/backup2l
fi
backup2l-1.6/debian/rules 0000775 0000000 0000000 00000002745 12706673016 0015405 0 ustar 00root root 0000000 0000000 #!/usr/bin/make -f
# Base on the sample debian/rules file for GNU Hello (1.3)
package=backup2l
build:
build-indep:
build-arch:
clean:
$(checkdir)
rm -rf *~ debian/tmp debian/*~ debian/files* debian/substvars
binary-indep: checkroot build
$(checkdir)
# There are no architecture-independent files to be uploaded
# generated by this package. If there were any they would be
# made here.
binary-arch: checkroot build
$(checkdir)
rm -rf debian/tmp
install -d debian/tmp/DEBIAN\
-d debian/tmp/usr/share/doc/$(package)\
-d debian/tmp/usr/share/man/man8\
-d debian/tmp/usr/sbin\
-d debian/tmp/etc/cron.daily
install -m 755 debian/prerm debian/tmp/DEBIAN
install -m 644 debian/conffiles debian/tmp/DEBIAN
./install-sh -fc debian/tmp/usr/share debian/tmp/usr/sbin debian/tmp/etc
cp -a debian/copyright debian/tmp/usr/share/doc/$(package)/
cp -a debian/changelog debian/tmp/usr/share/doc/$(package)/changelog
cp -a README.md debian/tmp/usr/share/doc/$(package)/
cp -a first-time.conf debian/tmp/usr/share/doc/$(package)/
cd debian/tmp/usr/share/doc/$(package) && gzip -9 changelog
cd debian/tmp/usr/share/doc/$(package) && gzip -9 README.md
dpkg-gencontrol
chown -R root.root debian/tmp
chmod -R g-ws debian/tmp
dpkg --build debian/tmp ..
define checkdir
test -f install-sh -a -f debian/rules
endef
# Below here is fairly generic really
binary: binary-indep binary-arch
checkroot:
$(checkdir)
test $$(id -u) = 0
.PHONY: binary binary-arch binary-indep clean checkroot build
backup2l-1.6/first-time.conf 0000664 0000000 0000000 00000040045 12706673016 0016030 0 ustar 00root root 0000000 0000000 ##################################################
# Configuration file for backup2l #
##################################################
# Define the backup2l version for which the configuration file is written.
# This way, future versions can automatically warn if the syntax has changed.
FOR_VERSION=1.5
##################################################
# Volume identification
# This is the prefix for all output files;
# multiple volumes can be handled by using different configuration files
VOLNAME="all"
##################################################
# Source files
# List of directories to make backups of.
# All paths MUST be absolute and start with a '/'!
SRCLIST=(/etc /root /home /var/mail /usr/local)
# The following expression specifies the files not to be archived.
# See the find(1) man page for further info. It is discouraged to
# use anything different from conditions (e. g. actions) as it may have
# unforeseeable side effects.
# This example skips all files and directories with a path name containing
# '.nobackup' and all .o files:
SKIPCOND=(-path "*.nobackup*" -o -name "*.o")
# Some background on 'SKIPCOND': The method of using a find(1) expression to determine
# files to backup or to skip is very powerful. Some of the following examples result from feature
# requests by various users who were not always aware that their "feature" was already implemented. ;-)
#
# If you want to exclude several directories use the following expression:
# SKIPCOND=(-path '/path1' -o -path '/path1/*' -o -path '/path2' -o -path '/path2/*')
#
# If you do not have anything to skip, use:
# SKIPCOND=(-false) # "SKIPCOND=()" does not work
#
# To skip directory trees (for performance reasons) you can add the '-prune' action to your SKIPCOND setting, e.g.:
# SKIPCOND=( -name "unimportant_dir" -prune )
#
# To prevent backup2l from crossing filesystem boundaries you can add '-xdev' or '-mount' to your SKIPCOND setting.
##################################################
# Destination
# Mount point of backup device (optional)
#BACKUP_DEV="/disk2"
# Destination directory for backups;
# it must exist and must not be the top-level of BACKUP_DEV
BACKUP_DIR="/disk2/backup"
##################################################
# Backup parameters
# Number of levels of differential backups (1..9)
MAX_LEVEL=3
# Maximum number of differential backups per level (1..9)
MAX_PER_LEVEL=8
# Maximum number of full backups (1..8)
MAX_FULL=2
# For differential backups: number of generations to keep per level;
# old backups are removed such that at least GENERATIONS * MAX_PER_LEVEL
# recent versions are still available for the respective level
GENERATIONS=1
# If the following variable is 1, a check file is automatically generated
CREATE_CHECK_FILE=1
##################################################
# Pre-/Post-backup functions
# This user-defined bash function is executed before a backup is made
PRE_BACKUP ()
{
echo " pre-backup: nothing to do"
# e. g., shut down some mail/db servers if their files are to be backup'ed
# On a Debian system, the following statements dump a machine-readable list of
# all installed packages to a file.
#echo " writing dpkg selections to /root/dpkg-selections.log..."
#dpkg --get-selections | diff - /root/dpkg-selections.log > /dev/null || dpkg --get-selections > /root/dpkg-selections.log
}
# This user-defined bash function is executed after a backup is made
POST_BACKUP ()
{
# e. g., restart some mail/db server if its files are to be backup'ed
echo " post-backup: nothing to do"
}
##################################################
# Misc.
# Create a backup when invoked without arguments?
AUTORUN=0
# Size units
SIZE_UNITS="" # set to "B", "K", "M" or "G" to obtain unified units in summary list
# Time zone for meta data
TIME_ZONE="UTC" # if unset (= ""), the local time zone is used for backup meta data;
# For new archives, the value "UTC" is recommended. However, older versions (<= 1.5) used local time,
# and changing the value causes backup2l to consider ALL files as new. So, change this value with care!
# Remove this line after the setup is finished.
UNCONFIGURED=1
# Archive driver for new backups (optional, default = "DRIVER_TAR_GZ")
# CREATE_DRIVER="DRIVER_MY_AFIOZ"
##################################################
# User-defined archive drivers (optional)
# This section demonstrates how user-defined archive drivers can be added.
# The example shows a modified version of the "afioz" driver with some additional parameters
# one may want to pass to afio in order to tune the speed, archive size etc. .
# An archive driver consists of a bash function named
# "DRIVER_" implementing the (sometimes simple) operations "-test", "-suffix",
# "-create", "-toc", and "-extract".
# If you do not want to write your own archive driver, you can remove the remainder of this file.
# USER_DRIVER_LIST="DRIVER_MY_AFIOZ" # uncomment to register the driver(s) below (optional)
DRIVER_MY_AFIOZ ()
{
case $1 in
-test)
# This function should check whether all prerequisites are met, especially if all
# required tools are installed. This prevents backup2l to fail in inconvenient
# situations, e. g. during a backup or restore operation. If everything is ok, the
# string "ok" should be returned. Everything else is interpreted as a failure.
require_tools afio
# The function 'require_tools' checks for the existence of all tools passed as
# arguments. If one of the tools is not found by which(1), an error message is
# displayed and the function does not return.
echo "ok"
;;
-suffix)
# This function should return the suffix of backup archive files. If the driver
# does not create a file (e. g. transfers the backup data immediately to a tape
# or network device), an empty string has to be returned. backup2l uses this suffix
# to select a driver for unpacking. If a user-configured driver supports the same
# suffix as a built-in driver, the user driver is preferred (as in this case).
echo "afioz"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
# This function is called to create a backup file. The argument $3 is the full file
# name of the archive file including path and suffix. $4 contains an alphabetically
# sorted list of files (full pathname) to be backed up. Directories are not contained,
# they are handled by backup2l directly without using the driver.
# All output to stderr should be directed to stdout ("2>&1").
afio -Zo -G 9 -M 30m -T 2k $3 < $4 2>&1
# This line passes some additional options to afio (see afio(1)):
# '-G 9' maximizes the compression by gzip.
# '-M 30m' increases the size of the internal file buffer. Larger files have to
# be compressed twice.
# '-T 2k' prevents the compression of files smaller than 2k in order to save time.
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
# This function is used to validate the correct generation of an archive file.
# The output is compared to the list file passed to the '-create' function.
# Any difference is reported as an error.
afio -Zt $3 | sed 's#^#/#'
# The sed command adds a leading slash to each entry.
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
# This function is called by backup2l's restore procedure for each archive.
# It is extremely important that only those files contained in $4 are restored.
# Otherwise it may happen that files are overwritten by incorrect (e. g. older)
# versions of the same file.
afio -Zinw $4 $3 2>&1
;;
esac
}
##################################################
# More sample archive drivers (optional)
# This is an unordered collection of drivers that may be useful for you,
# either to use them directly or to derive own drivers.
# Here's a version of the standard DRIVER_TAR_GZ driver,
# modified to split the output archive file into multiple sections.
# (donated by Michael Moedt)
DRIVER_TAR_GZ_SPLIT ()
{
case $1 in
-test)
require_tools tar split cat
echo "ok"
;;
-suffix)
echo "tgz_split"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
mkdir -p ${3}
tar cz -T $4 --no-recursion | split --bytes=725100100 - ${3}/part_
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
cat ${3}/part_* | tar tz | sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
cat ${3}/part_* | tar xz --same-permission --same-owner -T $4 2>&1
;;
esac
}
# The following driver is equivalent to the built-in DRIVER_TAR_GZ driver, but
# does not change the access times of the original files during backup
# (Adrian Bunk, Gundolf Kiefer)
DRIVER_TAR_GZ ()
{
case $1 in
-test)
require_tools tar
echo "ok"
;;
-suffix)
echo "tar.gz"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar czf $3 -T $4 --no-recursion --atime-preserve 2>&1 \
| grep -v 'tar: Removing leading .* from .* names'
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
tar tzf $3 | sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar zx --same-permission --same-owner -f $3 -T $4 2>&1
;;
esac
}
# This driver uses afio and bzip2, where bzip2 is invoked by afio.
# (donated by Carl Staelin)
DRIVER_MY_AFIOBZ2 ()
{
case $1 in
-test)
require_tools afio bzip2
echo "ok"
;;
-suffix)
echo "afio-bz2"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
afio -z -1 m -P bzip2 -Q -9 -Z -M 50m -T 1k -o $3 <$4 2>&1
# This line passes some additional options to afio (see afio(1)):
# '-P bzip2' utilizes bzip2 as an external compressor
# '-Q 9' maximizes the compression by bzip2.
# '-M 50m' increases the size of the internal file buffer. Larger files have to
# be compressed twice.
# '-T 1k' prevents the compression of files smaller than 1k in order to save time.
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
afio -t -Z -P bzip2 -Q -d - <$3 | sed 's#^#/#'
# The sed command adds a leading slash to each entry.
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
afio -Zinw $4 -P bzip2 -Q -d - <$3 2>&1
;;
esac
}
# This driver uses afio and bzip2, such that the I/O stream is piped through bzip2.
# (donated by Carl Staelin)
DRIVER_MY_AFIO_BZ2 ()
{
case $1 in
-test)
require_tools afio bzip2
echo "ok"
;;
-suffix)
echo "afio.bz2"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
afio -o - < $4 | bzip2 --best > $3 2>&1
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
bzip2 -d < $3 | afio -t - | sed 's#^#/#'
# The sed command adds a leading slash to each entry.
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
bzip2 -d < $3 | afio -inw $4 - 2>&1
;;
esac
}
# This driver uses the Info-ZIP tools to generate zip files. Unfourtunately unzip
# expects all file names to be on the command line. So unless there is a work-
# around it's not possible to use the "-extract" command.
# (donated by Georg Lutz)
DRIVER_ZIP ()
{
case $1 in
-test)
require_tools zip
echo "ok"
;;
-suffix)
echo "zip"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
cat $4| zip -qy $3 -@
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
zipinfo -1 $3| sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
echo "Not implemented yet! Sorry."
#unzip $3
;;
esac
}
# This driver uses tar and pipes the output trough gnupg. You can specifiy
# the passphrase in a file (/etc/backup2l.pass in the example). You have to
# invoke gpg at least one time before backup because gnupg has to initiate
# first thing in the home directory.
DRIVER_TAR_GPG ()
{
case $1 in
-test)
require_tools tar gpg
echo "ok"
;;
-suffix)
echo "tar.pgp"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar -c -T $4 --no-recursion | /usr/bin/gpg --batch --no-tty -q --passphrase-fd 3 3 $3
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
/usr/bin/gpg --batch --no-tty -q --passphrase-fd 3 3/dev/null | tar t | sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
/usr/bin/gpg --batch --no-tty -q --passphrase-fd 3 3/dev/null | tar -x --same-permission --same-owner -f $3 -T $4 2>&1
;;
esac
}
# PIGZ driver (donated by Thomas R. Bailey )
#
# NOTES: USE ONLY WITH MULTI CORE CPU
# REQUIRES YOU TO DOWNLOAD AND COMPILE PIGZ
# OR INSTALL IT FROM YOUR DISTRO REPOSITORY
# http://www.zlib.net/pigz/
#
DRIVER_TAR_GZ_PIGZ () {
case $1 in
-test)
require_tools tar gzip pigz cat
echo "ok"
;;
-suffix)
echo "tar.gz"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar cf - --files-from=$4 | pigz --best > $3 2>&1
;;
# Using tar tz for the toc verifies the PIGZ archive is usable
-toc) # Arguments: $2 = BID, $3 = archive file name
cat $3 | tar tz | sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
# Uncomment the next line and comment the line below it to uncompress using tar instead of pigz
# cat $3 | tar xz --same-permission --same-owner -T $4 2>&1
cat $3 | pigz -d | tar x --same-permission --same-owner --files-from=$4 2>&1
;;
esac
}
# This driver uses tar and LZMA (lzip) compression. LZMA compresses better than
# bzip2, but at the expense of more memory usage. (donated by Amedee Van Gasse)
DRIVER_TAR_LZ ()
{
case $1 in
-test)
require_tools tar lzip
echo "ok"
;;
-suffix)
echo "tar.lz"
;;
-create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
tar -c -T $4 --no-recursion | lzip --best -c > $3 2>&1 \
| grep -v 'tar: Removing leading .* from .*'
;;
-toc) # Arguments: $2 = BID, $3 = archive file name
lzip -d $3 -c | tar t | sed 's#^#/#'
;;
-extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file
lzip -d $3 -c | tar -x --same-permission --same-owner -T $4 2>&1
;;
esac
}
backup2l-1.6/install-sh 0000775 0000000 0000000 00000006615 12706673016 0015107 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
NO_PROMPT=0
TOUCH_CONF=1
UNINST=0
case $1 in
-f)
NO_PROMPT=1
TOUCH_CONF=0
shift
;;
-fc)
NO_PROMPT=1
shift
;;
-u)
NO_PROMPT=1
UNINST=1
TOUCH_CONF=0
shift
;;
-uc)
NO_PROMPT=1
UNINST=1
shift
;;
esac
if [[ "$1" != "" ]]; then
PREFIX=${1%/}
else
PREFIX="/usr/local"
fi
if [[ "$2" != "" ]]; then
PREFIX_BIN=${2%/}
else
PREFIX_BIN="$PREFIX/bin"
fi
if [[ "$3" != "" ]]; then
PREFIX_CONF=${3%/}
else
PREFIX_CONF="/etc"
fi
if [[ "$NO_PROMPT" == "0" ]]; then
cat << EOF
Usage: install-sh [ -f | -fc | -u | -uc ] [ ] [ ] [ ]
Where
-f: (Re-)Install program
-fc: (Re-)Install program and configuration files
-u: Uninstall program
-uc: Uninstall program and configuration files
: Location (default: /usr/local)
: Location for binary files (default: /bin)
: Location for configuration files (default: /etc)
EOF
fi
CRON_FILE=""
if [[ "$TOUCH_CONF" == "1" ]]; then
if [ -d $PREFIX_CONF/cron.daily ]; then
# standard cron/daily path for some Linux's...
CRON_FILE=$PREFIX_CONF/cron.daily/zz-backup2l
elif [ -d $PREFIX_CONF/periodic/daily ]; then
# standard cron/daily path for Mac OS X...
CRON_FILE=$PREFIX_CONF/periodic/daily/zz-backup2l
fi
fi
PROG_FILES="$PREFIX_BIN/backup2l $PREFIX/man/man8/backup2l.8.gz"
CONF_FILES="$PREFIX_CONF/backup2l.conf $CRON_FILE"
if [[ "$UNINST" == "0" ]]; then
# Installation...
if [[ "$NO_PROMPT" == "0" ]]; then
echo -e "I am about to install the following program file(s):\n $PROG_FILES\n"
read -p "Do you want to continue? [y/N] " ANSWER
echo
if [[ "$ANSWER" != "y" ]]; then
echo "Stopping."
exit 1
fi
fi
mkdir -p $PREFIX_BIN $PREFIX/man/man8
cp -af backup2l $PREFIX_BIN
gzip -9 -c backup2l.8 > $PREFIX/man/man8/backup2l.8.gz
echo "Program files installed."
if [[ "$TOUCH_CONF" == "1" && "$NO_PROMPT" == "0" ]]; then
echo -e "\nI can install the following configuration file(s):\n $CONF_FILES\n"
echo "This is recommended for a first-time installation."
echo -e "Warning: Previously existing files will be overwritten!\n"
read -p "Do you want to continue? [y/N] " ANSWER
echo
if [[ "$ANSWER" != "y" ]]; then
TOUCH_CONF=0
fi
fi
if [[ "$TOUCH_CONF" == "1" ]]; then
mkdir -p $PREFIX_CONF
cp -af first-time.conf $PREFIX_CONF/backup2l.conf
echo "Configuration files installed."
if [[ "$CRON_FILE" != "" ]]; then
cp -af zz-backup2l $CRON_FILE
else
echo -e "\nNote: No suitable cron directory found - file 'zz-backup2l' not (un-)installed automatically."
fi
else
echo "No configuration files installed."
fi
else
# Un-installation...
echo "Removing program file(s): $PROG_FILES"
rm -f $PROG_FILES
if [[ "$TOUCH_CONF" == "1" ]]; then
echo "Removing configuration file(s): $CONF_FILES"
rm -f $CONF_FILES
if [[ "$CRON_FILE" == "" ]]; then
echo -e "\nNote: Cron directory not found - eventually remove 'zz-backup2l' manually."
fi
else
echo "Configuration files NOT removed."
fi
fi
backup2l-1.6/zz-backup2l 0000775 0000000 0000000 00000000510 12706673016 0015161 0 ustar 00root root 0000000 0000000 #!/bin/bash
# The following command invokes 'backup2l' with the default configuration
# file (/etc/backup2l.conf).
#
# (Re)move it or this entire script if you do not want automatic backups.
#
# Redirect its output if you do not want automatic e-mails after each backup.
! which backup2l > /dev/null || nice -n 19 backup2l -b