backup2l-1.7/0000755000175000017500000000000014303744020012033 5ustar adjooadjoobackup2l-1.7/debian/0000755000175000017500000000000014303740152013257 5ustar adjooadjoobackup2l-1.7/debian/conffiles0000644000175000017500000000005714303740152015154 0ustar adjooadjoo/etc/backup2l.conf /etc/cron.daily/zz-backup2l backup2l-1.7/debian/control0000644000175000017500000000277214303740152014672 0ustar adjooadjooSource: 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.7/debian/copyright0000644000175000017500000000156614303740152015222 0ustar adjooadjooCopyright (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.7/debian/prerm0000644000175000017500000000017614303740152014333 0ustar adjooadjoo#!/bin/bash set -e if [ \( "$1" = "upgrade" -o "$1" = "remove" \) -a -L /usr/doc/backup2l ]; then rm -f /usr/doc/backup2l fi backup2l-1.7/debian/rules0000755000175000017500000000274514303740152014347 0ustar adjooadjoo#!/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.7/debian/changelog0000644000175000017500000001237414303740152015140 0ustar adjooadjoobackup2l (1.7) unstable; urgency=low * tar-based drivers: Fix unreported errors * A detailed upstream changelog can be found at https://github.com/gkiefer/backup2l. -- Gundolf Kiefer Tue, 30 Aug 2022 13:23:50 +0200 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.7/LICENSE0000644000175000017500000004317714303740152013056 0ustar adjooadjoo 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.7/_config.yml0000644000175000017500000000003314303740152014160 0ustar adjooadjootheme: jekyll-theme-minimalbackup2l-1.7/backup2l.80000644000175000017500000003723614303744020013642 0ustar adjooadjoo.TH "BACKUP2L" "8" "August 31, 2022" "backup2l v1.7" .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 can be used to skip subdirs on other file systems such as /dev or /proc, or \fB\-L\fP will cause all symbolic links to be followed (use with care). .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.7/install-sh0000755000175000017500000000661514303740152014051 0ustar adjooadjoo#!/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.7/zz-backup2l0000755000175000017500000000051014303740152014123 0ustar adjooadjoo#!/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 backup2l-1.7/backup2l0000755000175000017500000012124014303743724013476 0ustar adjooadjoo#!/bin/bash # backup2l --- low-maintenance backup tool # Copyright (c) 2001-2022 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.7 # 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; s#\\\\\\\\#\\\\#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 --no-recursion -T $4 2>&1 \ | grep -v 'tar: Removing leading .* from .*' ;; -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 -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 --no-recursion -T $4 2>&1 \ | grep -v 'tar: Removing leading .* from .*' ;; -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 --no-recursion -T $4 2>&1 \ | grep -v 'tar: Removing leading .* from .*' ;; -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 -ax | 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 |%7i %7i |%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 | tee $TMP.error | 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 -a $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 "s#^$ARCH: /##" \ > $BACKUP_DIR/$TMP.curlist 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.7/README.md0000644000175000017500000001322414303743630013322 0ustar adjooadjoo# *backup2l* - A Low-Maintenance Backup/Restore Tool by Gundolf Kiefer, 2001-2022 ## 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 v1.6 by Gundolf Kiefer Wed Nov 6 07:58:00 CET 2019 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 ... Wed Nov 6 07:58:41 CET 2019 Summary ======= Archive Date | Size (KB) | Skipped Files+Dirs | New Obs. | Errors ------------------------------------------------------------------------------ all.1 2019-07-15 | 1475248 | 620 17795 |17795 0 | 45 all.101 2019-07-27 | 235720 | 617 17002 | 812 1605 | 2 all.102 2019-08-19 | 210648 | 626 23446 | 7077 633 | 1 all.103 2019-08-30 | 125396 | 670 23796 | 1963 1613 | 0 all.104 2019-09-07 | 203880 | 669 25597 | 5353 3552 | 1 all.105 2019-09-20 | 87076 | 409 24333 | 2192 3456 | 6 all.106 2019-10-01 | 88492 | 409 27611 | 4591 1313 | 1 all.107 2019-10-12 | 67624 | 409 26589 | 1194 2216 | 0 all.108 2019-10-21 | 111580 | 506 26553 | 1351 1387 | 0 all.1081 2019-10-21 | 948 | 506 26554 | 70 69 | 0 all.1082 2019-10-22 | 164 | 506 26554 | 90 90 | 0 all.1083 2019-10-23 | 46680 | 506 27814 | 1465 205 | 0 all.1084 2019-10-24 | 29672 | 506 28578 | 1111 347 | 0 all.1085 2019-10-25 | 28272 | 506 26869 | 282 1991 | 0 all.1086 2019-10-26 | 45000 | 506 26866 | 314 317 | 0 all.1087 2019-10-27 | 33136 | 506 27162 | 610 314 | 0 all.1088 2019-10-28 | 12280 | 506 27645 | 673 190 | 0 all.11 2019-10-29 | 822056 | 506 27725 |13582 3652 | 8 all.1101 2019-10-30 | 20888 | 506 23707 | 858 4876 | 0 all.1102 2019-10-31 | 11336 | 512 26807 | 3372 272 | 0 all.1103 2019-11-05 | 312 | 512 26786 | 173 194 | 0 all.1104 2019-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 directory from snapshot into working directory** Restoring the directory */home/home/gundolf/prog/* from snapshot ** into the current working directory: ``` lilienthal:/scratch# backup2l -t 1102 -r /home/gundolf/prog/ backup2l v1.6 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 * Releases from 1.5 upwards are git-tagged accordingly. * Git repository: https://github.com/gkiefer/backup2l backup2l-1.7/first-time.conf0000644000175000017500000004155314303744020014775 0ustar adjooadjoo################################################## # 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.7 ################################################## # 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 '/'! # # The last elements of the list may be options for find(1). For example '-xdev' # can be used to skip subdirs on other file systems such as '/dev' or '/proc', # or '-L' will cause all symbolic links to be followed (use with care). # 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 ) ################################################## # 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 ) # (improved by Sven Hartge ) # # 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_PIGZ () { case $1 in -test) require_tools tar pigz gzip echo "ok" ;; -suffix) echo "tar.gz" ;; -create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file tar cf $3 -T $4 -I pigz --no-recursion --atime-preserve 2>&1 | grep -v 'tar: Removing leading .* from .* names' ;; -toc) # Arguments: $2 = BID, $3 = archive file name tar tf $3 -I pigz | sed 's#^#/#' ;; -extract) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file tar x --same-permission --same-owner -I pigz -f $3 -T $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 } # This driver uses tar and XZ compression. XZ compresses better than # bzip2, but at the expense of more memory usage. (donated by Sven Hartge) DRIVER_TAR_XZ () { case $1 in -test) require_tools tar xz echo "ok" ;; -suffix) echo "tar.xz" ;; -create) # Arguments: $2 = BID, $3 = archive file name, $4 = file list file tar cJf $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 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 }