Subversion Repositories general

Rev

Rev 1228 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1149 dev 1
#!/usr/bin/perl
2
# fsbackup - file system backup and synchronization utility. 
3
#
4
# http://www.opennet.ru/dev/fsbackup/
5
# Copyright (c) 2001-2002 by Maxim Chirkov. <mc@tyumen.ru>
6
#
1153 dev 7
# Ключи:
8
# -n - создаем новый архив независимо от состояния хэша.
9
# -f - full_backup - полный бэкап в архив, без хэша.
10
# -h - hash - только генерация хэша, без помещения файлов в архив.
11
# -c - clean - очиска хранилища с инкрементальным бэкапом и создание нового бэкапа.
1206 dev 12
#
13
# $Id: fsbackup.pl 1253 2006-06-19 14:45:36Z dev $
14
#
1149 dev 15
#############################################
1154 dev 16
use constant DB_DEF_CACHE_SIZE => 40960000; # Размер кэша для размежения хэша в памяти
1149 dev 17
 
18
use POSIX;
19
use File::Find;
20
use Digest::MD5 qw(md5_base64);
21
use Net::FTP;
22
use DB_File;
1205 dev 23
use BSD::stat;
1149 dev 24
 
25
use constant VERB_SILENT => 0; # Silent mode, suspend all output.
26
use constant VERB_ERROR => 1; # Output all errors and warnings.
27
use constant VERB_ALL => 2; # Output all the  available  data.
28
 
1154 dev 29
my $version = "2.0";
1149 dev 30
my $list_lines_cnt = 0;
31
my $del_lines_cnt = 0;
32
my $cur_time = time();
33
my %active_hash_last;
34
my %active_hash_new;
35
my $cfg_new_flag = 0;
36
my $cfg_clean_flag = 0;
37
my $config = 0;
1153 dev 38
my $cur_backup_size = 1536; # Размер блока tar
1149 dev 39
my $backup_file_base;
40
my $prog_pgp_filter;
41
my $prog_gzip_filter;
42
my $arc_ext;
43
my $ftp;
44
my $cur_increment_level;
45
my $cur_dir;
46
my $cur_path;
47
my $cur_file;
48
my $cur_pathitem;
49
my $file_fullpath;
50
my $file_fullpath_md5;
51
my $key;
52
my $dbobj_new;
53
my $dbobj_last;
54
my $db_hashinfo;
55
my $db_hashinfo2;
56
my $file;
57
my @volume_position=(0);
1153 dev 58
my @fs_path=();	       #  /dir[/file] - путь к файлу/директории для бэкапа.
59
my @fs_notpath=();     #  ! - отрицание пути, не помещать в бэкап. Всегда должен быть первым символом.
60
my @fs_mask=();        #  =~ - маска для файла или директории, а не абсолютный путь. Первый или второй символ.
61
my @fs_filemask=();    #  f~ - маска для файла. Первый или второй символ.
62
my @fs_dirmask=();     #  d~ - маска для директории. Первый или второй символ.
63
my @fs_notmask=();     #  =! - "НЕ" маска для файла или директории, а не абсолютный путь. Первый или второй символ.
64
my @fs_notfilemask=(); #  f! - "НЕ" маска для файла. Первый или второй символ.
65
my @fs_notdirmask=();  #  d! - "НЕ" маска для директории. Первый или второй символ.
1149 dev 66
 
1153 dev 67
# ------------- Обработка параметров командной строки
1149 dev 68
 
69
if ($ARGV[0] eq "-n" || $ARGV[0] eq "-h" || $ARGV[0] eq "-f" || $ARGV[0] eq "-c"){
70
    $cfg_new_flag=1;
71
    $config = $ARGV[1];
72
} else {
73
    $cfg_new_flag=0;
74
    $config = $ARGV[0];
75
}
76
 
77
if ( ! -f $config){
78
    die "Usage: fsbackup.pl [-n|-f|-h|-c] config_name\n";
79
}
80
 
81
require "$config";
82
 
1154 dev 83
$cfg_move_old_backup=1 if(!defined($cfg_move_old_backup));
84
$cfg_exit_on_empty=1   if(!defined($cfg_exit_on_empty));
85
 
1149 dev 86
if ( ! -d $cfg_cache_dir){
87
    die "\$cfg_cache_dir ($cfg_cache_dir) not found. Set \$cfg_cache_dir varisble in fsbackup.pl\n";
88
}
89
 
1153 dev 90
$cfg_time_limit *= 60 * 60 * 24; # Дни в секунды.
91
$cfg_size_limit *= 1024;	 # Килобайты в байты.
92
$cfg_maximum_archive_size *= 1024;	 # Килобайты в байты.
1149 dev 93
 
1151 dev 94
chdir($cfg_root_path);
1149 dev 95
 
96
if ($ARGV[0] eq "-h"){
97
    $cfg_backup_style = "hash";
98
}
99
if ($ARGV[0] eq "-f" ){
100
    $cfg_backup_style = "full_backup";
101
}
102
 
103
if ($ARGV[0] eq "-c" ){
104
    $cfg_clean_flag=1;
105
} else {
106
    $cfg_clean_flag=0;
107
}
108
 
1153 dev 109
#------------------- Проверяем переменные в файле конфигурации.
1206 dev 110
if ($cfg_backup_name !~ /^[\w\d\_.]+$/){
1149 dev 111
    die "Found illegal characters in $cfg_backup_name ($cfg_backup_name).";
112
}
113
 
114
if (! grep {$_ eq $cfg_checksum} ("md5", "timesize")){
115
    die "Unknown checksum method:\$cfg_checksum=$cfg_checksum (allowed md5 or timesize)\n";
116
}
117
 
118
if (! grep {$_ eq $cfg_backup_style} ("backup", "full_backup", "sync", "hash")){
119
    die "Unknown backup_style:\$cfg_backup_style=$cfg_backup_style\n";
120
}
121
 
122
 
123
if ($cfg_backup_style eq "full_backup" || $cfg_backup_style eq "hash"){
124
    $cfg_new_flag=1;
125
    $cfg_clean_flag=1;
126
}
127
 
128
if (! grep {$_ eq $cfg_type} ("local", "remote_ssh", "remote_ftp")){
129
    die "Unknown backup target:\$cfg_type=$cfg_type\n";
130
}
131
 
132
if (($cfg_type eq "local") && (! -d $cfg_local_path)){
133
    die "Can't find \$cfg_local_path ($cfg_local_path)";
134
}
135
 
136
if ($cfg_backup_style eq "backup"){
137
    my ($sec,$min,$hour,$mday,$mon,$year) = localtime($cur_time);
138
    $backup_file_base = sprintf ("%s-%4.4d.%2.2d.%2.2d.%2.2d.%2.2d.%2.2d",
139
		$cfg_backup_name,$year+1900,$mon+1,$mday,$hour,$min,$sec);
140
}else{
141
    $backup_file_base="$cfg_backup_name";
142
}
143
 
144
print "Creating $cfg_type $cfg_backup_style: $cfg_backup_name\n" if ($cfg_verbose == &VERB_ALL);
145
 
146
if ($cfg_pgp_userid ne "" && $prog_pgp ne ""){
147
    print "PGP: enabled\n" if ($cfg_verbose == &VERB_ALL);
148
 
149
#    PGP 2.6 (pgp)
150
#    $prog_pgp_filter="| $prog_pgp -ef $cfg_pgp_userid -z'$cfg_pgp_userid' ";
151
#    PGP 5.0 (pgpe)
152
#    $prog_pgp_filter="| $prog_pgp -f $cfg_pgp_userid";
153
#    GnuPG (pgp)
154
    $prog_pgp_filter="| $prog_pgp -v --batch -e -r $cfg_pgp_userid";
155
} else {
156
    $prog_pgp_filter="";
157
}
158
 
159
if ($prog_gzip ne ""){
160
    $prog_gzip_filter="| $prog_gzip";
161
    $arc_ext=".gz";
162
} else {
163
    $prog_gzip_filter="";
164
    $arc_ext="";
165
 
166
}
167
 
168
if (! -d "$cfg_cache_dir/$cfg_backup_name"){
169
    mkdir("$cfg_cache_dir/$cfg_backup_name", 0700);
170
}
171
 
1153 dev 172
# ---------- Активируем FTP соединение 
1149 dev 173
 
174
ftp_connect();
175
 
1153 dev 176
#----------- Вычисляем уровень инкрементальности.
1149 dev 177
if ($cfg_increment_level != 0 && $cfg_backup_style eq "backup"){
1154 dev 178
    if(open(INCREMENT_LEVEL, "<$cfg_cache_dir/$cfg_backup_name/.increment_level")) {
179
        $cur_increment_level = <INCREMENT_LEVEL>;
180
        $cur_increment_level++;
181
        close (INCREMENT_LEVEL);
182
    }
183
    else {
184
        print "Can't open increment level file ($cfg_cache_dir/$cfg_backup_name/.increment_level).\n";
185
        $cur_increment_level = 0;
186
    }
1152 dev 187
 
1154 dev 188
    if ($cur_increment_level >= $cfg_increment_level){
189
        $cur_increment_level=0;
190
    }
1152 dev 191
 
1154 dev 192
    if ($cur_increment_level == 0){
1149 dev 193
	$cfg_new_flag=1;
194
	$cfg_clean_flag=1;
195
    }
1154 dev 196
    if(open(INCREMENT_LEVEL, ">$cfg_cache_dir/$cfg_backup_name/.increment_level")) {
197
        print INCREMENT_LEVEL $cur_increment_level;
198
        close (INCREMENT_LEVEL);
199
    }
200
    else {
201
        print "Can't save increment level to file ($cfg_cache_dir/$cfg_backup_name/.increment_level).\n";
202
    }
1149 dev 203
    print "Current increment number: $cur_increment_level\n" if ($cfg_verbose == &VERB_ALL);
204
}
205
################################################
1153 dev 206
#----------- Считываем хэш в память.
1149 dev 207
 
208
if ( (-f "$cfg_cache_dir/$cfg_backup_name/.hash" || $cfg_type ne "local" ) && $cfg_new_flag == 0){
1153 dev 209
# Считываем текущий хеш в память.
1149 dev 210
 
211
if ( $cfg_type eq "local"){
212
    rename ("$cfg_cache_dir/$cfg_backup_name/.hash", "$cfg_cache_dir/$cfg_backup_name/.hash.last");
213
}elsif ( $cfg_type eq "remote_ssh"){
214
    system ("$prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat $cfg_remote_path/.hash' > $cfg_cache_dir/$cfg_backup_name/.hash.last") == 0 || print "SSH connection failed: $?\n";
215
} elsif ( $cfg_type eq "remote_ftp"){
216
    unlink ("$cfg_cache_dir/$cfg_backup_name/.hash.last");
217
    $ftp->get(".hash", "$cfg_cache_dir/$cfg_backup_name/.hash.last")|| print "FTP error, Can't GET .hash\n";
218
}
219
        $db_hashinfo = new DB_File::HASHINFO ;
220
        $db_hashinfo->{'cachesize'} =  DB_DEF_CACHE_SIZE;
221
        if (! ($dbobj_last = tie(%active_hash_last, "DB_File", "$cfg_cache_dir/$cfg_backup_name/.hash.last", O_RDWR|O_CREAT, 0644, $db_hashinfo ))){
222
	    print "WARNING: Error in hash, creating full backup.\n" if ($cfg_verbose >= &VERB_ERROR);
223
	    unlink "$cfg_cache_dir/$cfg_backup_name/.hash.last";
224
	    $dbobj_last = tie(%active_hash_last, "DB_File", "$cfg_cache_dir/$cfg_backup_name/.hash.last", O_RDWR|O_CREAT, 0644, $db_hashinfo )||print "Can't create or open DB File!";
225
	}
226
	# $dbobj->del($key);
227
	# $dbobj->sync();
228
 
229
}
230
 
1153 dev 231
# Закрываем ftp соединение. Следующий блок может выполняться гораздо дольше 
232
# чем таймаут ftp.
1149 dev 233
if ( $cfg_type eq "remote_ftp"){
234
    $ftp->quit;
235
}
1153 dev 236
#Создаем новый хеш.
1149 dev 237
unlink("$cfg_cache_dir/$cfg_backup_name/.hash");
238
$db_hashinfo2 = new DB_File::HASHINFO ;
239
$db_hashinfo2->{'cachesize'} =  100000;
240
$dbobj_new = tie(%active_hash_new, "DB_File", "$cfg_cache_dir/$cfg_backup_name/.hash", O_RDWR|O_CREAT, 0644, $db_hashinfo2) || print "Can't create or open DB File!\n";
241
 
1153 dev 242
# Создаем список файлов для помещения в архив.
1149 dev 243
open (LIST, ">$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list")|| print "Can't create list file ($cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list).\n";
244
flock (LIST, 2);
245
 
1153 dev 246
# Создаем список директорий в архиве.
1149 dev 247
open (DIRS, ">$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir")|| print "Can't create list file ($cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir).\n";
248
flock (DIRS, 2);
249
 
1154 dev 250
open (META, ">$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.meta")|| print "Can't create meta file ($cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.meta).\n";
251
print META "fsbackup $version\n";
252
print META "increment level: $cur_increment_level\n";
253
close(META);
254
 
1153 dev 255
# Считываем список подлежащих бэкапу директорий в память.
1151 dev 256
 
1149 dev 257
while(<DATA>){
258
    chomp;
259
    $cur_path = $_;
260
    if ($cur_path =~ /^\!(.*)$/){		#  !
261
	push @fs_notpath, $1;
262
 
263
    } elsif ($cur_path =~ /^\=\~(.*)$/){	#  =~
264
	push @fs_mask, $1;
265
 
266
    } elsif ($cur_path =~ /^f\~(.*)$/){		#  f~
267
	push @fs_filemask, $1;
268
 
269
    } elsif ($cur_path =~ /^d\~(.*)$/){		#  d~
270
	push @fs_dirmask, $1;
271
 
272
    } elsif ($cur_path =~ /^\=\!(.*)$/){	#  =!
273
	push @fs_notmask, $1;
274
 
275
    } elsif ($cur_path =~ /^f\!(.*)$/){		#  f!
276
	push @fs_notfilemask, $1;
277
 
278
    } elsif ($cur_path =~ /^d\!(.*)$/){		#  d!
279
	push @fs_notdirmask, $1;
280
 
281
    } elsif ($cur_path =~ /^#/ || $cur_path =~ /^\s*$/){ #  comment
282
	next;
283
 
284
    } elsif ($cur_path =~ /[\/\w]+/) {		#  /dir[/file]
1151 dev 285
	push @fs_path, $cur_path;
1149 dev 286
 
287
    } else {
288
	print STDERR "Syntax error: $cur_path, ingnored.\n" if ($cfg_verbose >= &VERB_ALL);
289
    }
290
}
291
 
292
#--------------------------------------------------------------------
1153 dev 293
# Последовательно просматририваем весь список директорий отмеченных для бэкапа
1149 dev 294
 
295
 
296
foreach $cur_pathitem (@fs_path){
297
    print "Adding $cur_pathitem....\n" if ($cfg_verbose == &VERB_ALL);
298
    find (\&add_to_backup, $cur_pathitem);
299
    print "done\n" if ($cfg_verbose == &VERB_ALL);
300
}
301
close (LIST);
302
close (DIRS);
303
#------------
1153 dev 304
# Составляем список удаленных файлов.
1149 dev 305
 
306
    open (DEL, ">$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del")|| print "Can't create list file ($cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del).\n";
307
    flock (DEL, 2);
308
    if ($cfg_backup_style ne "hash"){
309
	while(($file, $key)= each(%active_hash_last)){
310
	    $file =~ s/\'/\'\\\'\'/g;
311
	    $file =~ s/^\/(.*)$/$1/;
312
	    print DEL "rm -rf '$file'\n";
313
	    $del_lines_cnt++;
314
	}
315
    }
316
    close(DEL);
317
 
1153 dev 318
# Записываем хэш на диск.
1149 dev 319
$dbobj_new->sync();
320
untie %active_hash_new;
321
untie %active_hash_last;
322
 
1153 dev 323
chdir ("/"); # Переходим в корень, так как все пути у нас без корневого /
1149 dev 324
 
1153 dev 325
# Активируем FTP соединение второй раз.
1149 dev 326
ftp_connect();
327
 
328
#------------
1153 dev 329
# Если только обновляем хэш, то записываем его и выходим.
1149 dev 330
 
1153 dev 331
if ($cfg_backup_style eq "hash"){ # Только создать хэшь без архивирования.
1149 dev 332
 
333
    if ( $cfg_type eq "local"){
334
	system( "cp -f $cfg_cache_dir/$cfg_backup_name/.hash $cfg_local_path/.hash") == 0 || print "Local FS copy hash failed: $?";
335
    } elsif ( $cfg_type eq "remote_ssh"){
336
	system( "cat $cfg_cache_dir/$cfg_backup_name/.hash | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.hash'") == 0 || print "SSH connection failed (copy hash): $?\n";
337
    } elsif ( $cfg_type eq "remote_ftp"){
338
	$ftp->delete(".hash");
339
	$ftp->put("$cfg_cache_dir/$cfg_backup_name/.hash", ".hash")|| print "Can't upload .hash to remote server via FTP\n";
340
    }
341
    exit (0);
342
}
343
 
344
#------------
1153 dev 345
# Архивируем и передаем в хранилище.
1149 dev 346
 
1154 dev 347
if ($cfg_exit_on_empty == 1 && $list_lines_cnt == 0 && $del_lines_cnt == 0){
348
print "$cfg_exit_on_empty\n";
1149 dev 349
    print "WARNING: Nothing to backup.\n" if ($cfg_verbose >= &VERB_ALL);
350
    exit;
351
}
352
if ( $cfg_type eq "local"){
353
 
354
    print "Storing local backup...\n" if ($cfg_verbose == &VERB_ALL);
355
    if ($cfg_backup_style eq "sync"){
1153 dev 356
	if ($cfg_clean_flag == 1){ # Удалить старые копии
1151 dev 357
	    print "WARNING: If you really shure to delete $cfg_local_path before sync operatioun uncomment line 'system( \"find \$cfg_local_path -not -path '\$cfg_local_path' -maxdepth 1 -exec \$prog_rm -rf \{\} \\;\");'" if ($cfg_verbose >= &VERB_ALL);
358
#    	    system( "find $cfg_local_path -not -path '$cfg_local_path' -maxdepth 1 -exec $prog_rm -rf \{\} \\;");
1149 dev 359
	}
360
 
361
	system( "cd $cfg_local_path; sh $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del");
362
	system( "$prog_tar -c -f - -T $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list| $prog_tar -xf - -C $cfg_local_path") == 0 || print "Local FS sync failed (tar|untar): $?\n";
363
	system( "cd $cfg_local_path; sh $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir");
364
	system( "cp -f $cfg_cache_dir/$cfg_backup_name/.hash $cfg_local_path/$backup_file_base.hash") == 0 || print "Local FS copy failed: $?\n";
365
 
366
    } else {
1153 dev 367
	if ($cfg_clean_flag == 1){ # Удалить старые копии
1149 dev 368
	    if ($cfg_save_old_backup == 0){
369
		system( "$prog_rm -f $cfg_local_path/*");
1154 dev 370
	    } elsif($cfg_move_old_backup == 1) {
1149 dev 371
		if (! -d "$cfg_local_path/OLD"){
372
		    system( "mkdir $cfg_local_path/OLD");
373
		}
374
		system( "$prog_rm -f $cfg_local_path/OLD/*");
375
		system( "mv -f $cfg_local_path/$cfg_backup_name* $cfg_local_path/OLD/");
376
		# system( "$prog_rm -f $cfg_local_path/*");
377
	    }
378
	}
1154 dev 379
	system( "cp -f $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.meta $cfg_local_path/$backup_file_base.meta") == 0 || print "Local FS .meta copy failed: $?\n";
1149 dev 380
	system( "cp -f $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list $cfg_local_path/$backup_file_base.list") == 0 || print "Local FS .list copy failed: $?\n";
381
	system( "cp -f $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir $cfg_local_path/$backup_file_base.dir") == 0 || print "Local FS .dir copy failed: $?\n";
382
	system( "cp -f $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del $cfg_local_path/$backup_file_base.del") == 0 || print "Local FS .del copy failed: $?\n";
1154 dev 383
	#system( "cp -f $cfg_cache_dir/$cfg_backup_name/.hash $cfg_local_path/$backup_file_base.hash") == 0 || print "Local FS .hash copy failed: $?\n";
1153 dev 384
	# Обрабатываем разбиение на тома
1149 dev 385
	for ($arc_block_level=0; $arc_block_level <= $#volume_position; $arc_block_level++){
1154 dev 386
	    my $tmp_list_file = create_tmp_list($arc_block_level, $volume_position[$arc_block_level], $volume_position[$arc_block_level+1], "$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list");
1149 dev 387
	    system( "$prog_tar -c -f - -T $tmp_list_file $prog_gzip_filter $prog_pgp_filter > $cfg_local_path/$backup_file_base-$arc_block_level.tar${arc_ext}") == 0 || print "Local FS tar backup failed: $?\n";
388
	}
389
    }
390
 
391
} elsif ( $cfg_type eq "remote_ssh"){
392
    print "Storing remote ssh backup...\n" if ($cfg_verbose == &VERB_ALL);
393
    if ($cfg_backup_style eq "sync"){
1153 dev 394
	if ($cfg_clean_flag == 1){ # Удалить старые копии
1151 dev 395
	    system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host find $cfg_remote_path -not -path '$cfg_remote_path' -maxdepth 1 -exec rm -rf \{\} \\;");
1149 dev 396
	}
397
	system( "cat $cfg_cache_dir/$cfg_backup_name/.hash | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.hash'") == 0 || print "SSH connection failed (store .hash): $?\n";
398
	system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.del'") == 0 || print "SSH connection failed (store .hash): $?\n";
399
	system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.dir'") == 0 || print "SSH connection failed (store .hash): $?\n";
400
        system("$prog_ssh -l $cfg_remote_login $cfg_remote_host '(cd $cfg_remote_path; sh .del)'");
401
        system( "$prog_tar -c -f - -T $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list $prog_gzip_filter| $prog_ssh -l $cfg_remote_login $cfg_remote_host tar -xf - -C $cfg_remote_path") == 0 || print "SSH connection failed (tar): $?\n";;
402
        system("$prog_ssh -l $cfg_remote_login $cfg_remote_host '(cd $cfg_remote_path; sh .dir)'");
403
 
404
 
405
	open (DEL, "<$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del");
406
	flock (DEL, 1);
407
	while(<DEL>){
408
	    chomp;
409
	    $cur_file = $_;
410
	    $cur_file =~ s/\'/\'\\\'\'/g;
411
    	    system("$prog_ssh -l $cfg_remote_login $cfg_remote_host rm -f '$cfg_remote_path/$cur_file'");
412
	}
413
	close(DEL);
414
    } else {
1153 dev 415
	if ($cfg_clean_flag == 1){ # Удалить старые копии
1149 dev 416
 
417
	    if ($cfg_save_old_backup == 0){
418
		system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host rm -f $cfg_remote_path/*");
419
	    } else {
420
		system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host '(if [ ! -d $cfg_remote_path/OLD ]; then mkdir $cfg_remote_path/OLD; fi)'");
421
		system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host rm -f $cfg_remote_path/OLD/*");
422
		system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host mv -f $cfg_remote_path/$cfg_backup_name* $cfg_remote_path/OLD/");
423
    		# system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host rm -f $cfg_remote_path/*");
424
	    }
425
	}
426
	system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base.list'") == 0 || print "SSH connection failed (copy .list): $?\n";
427
	system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base.dir'") == 0 || print "SSH connection failed (copy .dir): $?\n";
428
        system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base.del'") == 0 || print "SSH connection failed (copy .del): $?\n";
429
        system( "cat $cfg_cache_dir/$cfg_backup_name/.hash | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base.hash'") == 0 || print "SSH connection failed (copy .hash): $?\n";
430
	system( "cat $cfg_cache_dir/$cfg_backup_name/.hash | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.hash'") == 0 || print "SSH connection failed (cache .hash): $?\n";
1153 dev 431
	# Обрабатываем разбиение на тома
1149 dev 432
	for ($arc_block_level=0; $arc_block_level <= $#volume_position; $arc_block_level++){
1154 dev 433
	    my $tmp_list_file = create_tmp_list($arc_block_level, $volume_position[$arc_block_level], $volume_position[$arc_block_level+1], "$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list");
1149 dev 434
            system( "$prog_tar -c -f - -T $tmp_list_file $prog_gzip_filter $prog_pgp_filter| $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base-$arc_block_level.tar${arc_ext}'") == 0 || print "SSH connection failed (tar): $?\n";
435
	}
436
    }
437
} elsif ( $cfg_type eq "remote_ftp"){
438
    print "Storing remote ftp backup...\n" if ($cfg_verbose == &VERB_ALL);
439
 
440
    if ($cfg_backup_style eq "sync"){
441
	print "WARNING: Backup style 'sync' only allowed for local and remote_ssh storage.\n" if ($cfg_verbose >= &VERB_ALL);
442
    } else {
1153 dev 443
	if ($cfg_clean_flag == 1){ # Удалить старые копии
1149 dev 444
	    if ($cfg_save_old_backup == 0){
445
		foreach $cur_dir ($ftp->ls()){
446
    		    $ftp->delete($cur_dir);
447
		}
448
	    } else {
449
		$ftp->mkdir("$cfg_remote_path/OLD");
450
		$ftp->cwd("$cfg_remote_path/OLD");
451
		foreach $cur_dir ($ftp->ls()){
452
    		    $ftp->delete($cur_dir);
453
		}
454
		$ftp->cwd("$cfg_remote_path");
455
		foreach $cur_dir ($ftp->ls()){
456
		    if ($cur_dir =~ /$cfg_backup_name/){
457
    			$ftp->rename($cur_dir,"$cfg_remote_path/OLD/$cur_dir");
458
		    }
459
		}
460
		foreach $cur_dir ($ftp->ls()){
461
    		    $ftp->delete($cur_dir);
462
		}
463
	    }
464
	}
465
	$ftp->delete("$backup_file_base.list");
466
	$ftp->put("$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list", "$backup_file_base.list") || print "Can't PUT .list file to remote FTP server\n";
467
	$ftp->delete("$backup_file_base.dir");
468
	$ftp->put("$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir", "$backup_file_base.dir")|| print "Can't PUT .dir file to remote FTP server\n";
469
	$ftp->delete("$backup_file_base.del");
470
	$ftp->put("$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del", "$backup_file_base.del")|| print "Can't PUT .del file to remote FTP server\n";
471
        $ftp->delete("$backup_file_base.hash");
472
        $ftp->put("$cfg_cache_dir/$cfg_backup_name/.hash", "$backup_file_base.hash")|| print "Can't PUT old .hash file to remote FTP server\n";
473
	$ftp->delete(".hash");
474
	$ftp->put("$cfg_cache_dir/$cfg_backup_name/.hash", ".hash")|| print "Can't PUT new .hash file to remote FTP server\n";
1153 dev 475
	# Обрабатываем разбиение на тома
1149 dev 476
	for ($arc_block_level=0; $arc_block_level <= $#volume_position; $arc_block_level++){
1154 dev 477
	    my $tmp_list_file = create_tmp_list($arc_block_level, $volume_position[$arc_block_level], $volume_position[$arc_block_level+1], "$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list");
1149 dev 478
	    $ftp->delete("$backup_file_base-$arc_block_level.tar${arc_ext}");
479
	    open (TAR,"$prog_tar -c -f - -T $tmp_list_file $prog_gzip_filter $prog_pgp_filter|")|| print "tar failed: $?\n";
480
    	    flock(TAR,1);
481
	    $ftp->put(*TAR, "$backup_file_base-$arc_block_level.tar${arc_ext}")|| print "Can't store backup archive to remote FTP server.\n";
482
	    close(TAR);
483
	}
484
    	$ftp->quit;
485
    }
486
}
487
 
488
if ( $cfg_type eq "remote_ftp"){
489
    $ftp->quit;
490
}
491
print "***** Backup successful complete.\n" if ($cfg_verbose == &VERB_ALL);
492
exit (0);
493
 
494
 
495
########################################
1253 dev 496
sub add_to_backup
497
{
498
  my($file_name, $file_dir, $md5_checksum_stat, $checksum_stat, $file_fullpath_esc);
1205 dev 499
  my($tmp, $stat_mode, $stat_uid, $stat_gid, $stat_size, $stat_mtime, $stat_time, $stat_flags);
1149 dev 500
 
1253 dev 501
  $file_name         = $_;
502
  $file_fullpath     = $File::Find::name;
503
  $file_dir          = $File::Find::dir;
504
  $file_fullpath_esc = $file_fullpath;
1149 dev 505
  $file_fullpath_esc =~ s/\'/\'\\\'\'/g;
506
 
1153 dev 507
  # Создаем список директорий
1253 dev 508
  if((-d $file_fullpath) && (! -l $file_fullpath)) {
509
    if(check_path($file_dir, $file_name) == 1) {
510
      if($cfg_backup_style ne "hash") {
511
        ($tmp, $tmp, $stat_mode, $tmp, $stat_uid, $stat_gid, $tmp, $stat_size, $tmp, $stat_mtime,
512
          $stat_time, $tmp, $tmp, $tmp, $tmp, $tmp, $stat_flags, $tmp) = lstat($file_fullpath);
513
        $stat_mode         = sprintf ("%04o", $stat_mode & 07777);
514
        $file_fullpath_esc =~ s/^\/(.*)$/$1/;
515
        my ($sec,$min,$hour,$mday,$mon,$year) = localtime($stat_time);
516
        $stat_time = sprintf("%4.4d%2.2d%2.2d%2.2d%2.2d.%2.2d",
517
          $year+1900, $mon+1, $mday, $hour, $min, $sec);
518
        print DIRS "mkdir -p '$file_fullpath_esc'\n";
519
        print DIRS "chmod $stat_mode '$file_fullpath_esc'\n";
520
        print DIRS "chown $stat_uid:$stat_gid '$file_fullpath_esc'\n";
521
        print DIRS "touch -t $stat_time '$file_fullpath_esc'\n";
522
        if($stat_flags) {
523
          print DIRS "chflags "
524
            . ($stat_flags & UF_NODUMP    ? "nodump," : "")
525
            . ($stat_flags & UF_IMMUTABLE ? "uchg,"   : "")
526
            . ($stat_flags & UF_APPEND    ? "uappnd," : "")
527
            . ($stat_flags & UF_OPAQUE    ? "opaque," : "")
528
            . ($stat_flags & UF_NOUNLINK  ? "uunlnk," : "")
529
            . ($stat_flags & SF_ARCHIVED  ? "arch,"   : "")
530
            . ($stat_flags & SF_IMMUTABLE ? "schg,"   : "")
531
            . ($stat_flags & SF_APPEND    ? "sappnd," : "")
532
            . ($stat_flags & SF_NOUNLINK  ? "sunlnk," : "")
533
            . " '$file_fullpath_esc'\n";
534
        }
1228 dev 535
 
1253 dev 536
        unless($stat_flags & UF_NODUMP) {
537
          $cur_backup_size += int(length($file_fullpath)/100.0 + 1) * 512;
538
          if($cfg_maximum_archive_size > 0 && $cur_backup_size + 10240 >= $cfg_maximum_archive_size) {
539
            my $old_val = $cur_backup_size - $stat_size - int(length($file_fullpath)/100.0 + 1) * 512;
540
            my $tmp_pos = $#volume_position+1;
541
            print "Volume $tmp_pos Done. Size: $old_val\n" if($cfg_verbose == &VERB_ALL);
542
            $cur_backup_size = $stat_size + int(length($file_fullpath)/100.0 + 1) * 512 + 1536;
543
            push @volume_position, $list_lines_cnt;
544
          }
545
          $active_hash_new{$file_fullpath} = "D";
546
          check_update($file_fullpath, "D", $file_fullpath, $stat_size);
547
        }
548
        else {
549
          push @fs_notpath, "$file_dir/$file_name"; # if nodump - skip all subitems
550
          if($cfg_stopdir_prune == 1) {
551
            $File::Find::prune = 1;
552
            return;
553
          }
554
        }
1149 dev 555
      }
1253 dev 556
    }
557
    else {
558
      if($cfg_stopdir_prune == 1) {
559
        $File::Find::prune = 1;
560
        return;
561
      }
562
    }
563
  }
564
  # Работаем с файлами
565
  elsif((-f $file_fullpath) || (-l $file_fullpath)) {
566
    if(check_path($file_dir, $file_name) == 1) {
567
      ($tmp, $tmp, $stat_mode, $tmp, $stat_uid, $stat_gid, $tmp, $stat_size, $tmp, $stat_mtime,
568
        $stat_time, $tmp, $tmp, $tmp, $tmp, $tmp, $stat_flags, $tmp) = lstat($file_fullpath);
569
      unless($stat_flags & UF_NODUMP) {
570
        $checksum_stat= md5_base64("$stat_mtime/$stat_size/$stat_mode/$stat_uid/$stat_gid");
571
        # $file_fullpath_md5 = md5_base64($file_fullpath);
572
        $file_fullpath_md5 = $file_fullpath;
573
        if($cfg_time_limit != 0 && $cur_time - $cfg_time_limit > $stat_mtime) {
574
          print "Time limit: $cur_time - $cfg_time_limit > $stat_mtime, file $file_fullpath ignored.\n"
575
            if($cfg_verbose == &VERB_ALL);
576
          next;
577
        }
578
        if($cfg_size_limit != 0 && $cfg_size_limit < $stat_size) {
579
          print "Size limit: $cfg_size_limit < $stat_size, file $file_fullpath ignored.\n"
580
            if($cfg_verbose == &VERB_ALL);
581
          next;
582
        }
1149 dev 583
 
1253 dev 584
        if(($cfg_checksum eq "md5") && (! -l $file_fullpath)) {
585
          ($md5_checksum_stat, $tmp) = split(/\s+/, `$prog_md5sum '$file_fullpath_esc'`);
586
          $active_hash_new{$file_fullpath_md5} = "$checksum_stat/$md5_checksum_stat";
587
          check_update($file_fullpath, "$checksum_stat/$md5_checksum_stat", $file_fullpath_md5, $stat_size);
588
        } else {
589
          $active_hash_new{$file_fullpath} = $checksum_stat;
590
          check_update($file_fullpath, $checksum_stat, $file_fullpath, $stat_size);
1205 dev 591
        }
1253 dev 592
      }
1205 dev 593
    }
1253 dev 594
  }
1149 dev 595
}
596
 
597
###############################################
1153 dev 598
# Проверяем изменился ли файл или нет, если да апдейтим лог.
1149 dev 599
sub check_update{
600
     my ($file, $checksum, $filesum, $stat_size) = @_;
601
 
1154 dev 602
    if (($active_hash_last{$filesum} ne $checksum) && ($checksum ne "D")){
1149 dev 603
	if ($cfg_backup_style ne "hash"){
604
		$file =~ s/^\/(.*)$/$1/;
605
	        print LIST "$file\n";
606
 
1153 dev 607
	        # Обрабатываем случай разбиения гиганских архивов.
1149 dev 608
		if (-l "/$file"){
609
		    $stat_size = 0;
610
		}
611
	        $cur_backup_size += $stat_size + int(length($file)/100.0 + 1)*512;
612
#	  	print "$cur_backup_size:$stat_size:$file\n";
613
	        if ($cfg_maximum_archive_size > 0 && $cur_backup_size + 10240 >= $cfg_maximum_archive_size){
614
	        my $old_val = $cur_backup_size - $stat_size - int(length($file)/100.0 + 1)*512;
615
		my $tmp_pos= $#volume_position+1;
616
	        print "Volume $tmp_pos Done. Size: $old_val\n" if ($cfg_verbose == &VERB_ALL);
617
	        $cur_backup_size = $stat_size + int(length($file)/100.0 + 1)*512 + 1536;
618
	        push @volume_position, $list_lines_cnt;
619
	  }
620
 
621
	}
622
	$list_lines_cnt++;
623
    }
1154 dev 624
    if(($active_hash_last{$filesum} eq "D") && ($checksum ne "D")
625
      || ($active_hash_last{$filesum} ne "D") && ($checksum eq "D"))
626
    {
627
        # if old entry was a directory and now it's file or link or vice versa, leave it in hash 
628
        # to add it later to the delete list
1149 dev 629
    }
1154 dev 630
    else {
631
        delete $active_hash_last{$filesum};
632
        if (defined $dbobj_last){
633
	    $dbobj_last->del($filesum);
634
        }
635
    }
1149 dev 636
}
637
 
638
###############################################
1153 dev 639
# 0 - не добавлять файл
640
# 1 - добавть файл
1149 dev 641
 
642
sub check_path {
643
    my ($dir_name, $file_name) = @_;
644
    my ($item, $path);
645
 
646
    $path = "$dir_name/$file_name";
647
 
1151 dev 648
 
649
 
1149 dev 650
    foreach $item (@fs_notmask){
651
	if ($path =~ /$item/){
652
	    return 0;
653
	}
654
    }
655
 
656
    foreach $item (@fs_notfilemask){
657
	if ($file_name =~ /$item/){
658
	    return 0;
659
	}
660
    }
661
 
662
    foreach $item (@fs_filemask){
663
	if ($file_name =~ /$item/){
664
	    return 1;
665
	}
666
    }
667
 
668
    foreach $item (@fs_notdirmask){
669
	if ($dir_name =~ /$item/){
670
	    return 0;
671
	}
672
    }
673
 
674
 
675
    foreach $item (@fs_mask){
676
	if ($path =~ /$item/){
677
	    return 1;
678
	}
679
    }
680
 
681
    foreach $item (@fs_dirmask){
682
	if ($dir_name =~ /$item/){
683
	    return 1;
684
	}
685
    }
686
 
687
 
688
    foreach $item (@fs_notpath){
689
	if (($dir_name eq $item) || ($path eq $item) || ($dir_name =~ /^$item\//)){
690
	    return 0;
691
	}
692
    }
693
 
694
    return 1;
695
}
696
###############################################
1153 dev 697
# Устанавливаем соединение с удаленным сервером по FTP.
1149 dev 698
 
699
sub ftp_connect{
700
    if ( $cfg_type eq "remote_ftp"){
1151 dev 701
	$ftp = Net::FTP->new($cfg_remote_host, Timeout => 30, Debug => 0) || die "Can't connect to ftp server.\n";
1149 dev 702
	$ftp->login($cfg_remote_login, $cfg_remote_password) || die "Can't login to ftp server.\n";
703
        $ftp->cwd($cfg_remote_path) || die "Path $cfg_remote_path not found on ftp server.\n";
704
	$ftp->binary();    
705
    }
706
}
707
###############################################
1153 dev 708
# Содание списка файлов для помещения в определенный том многотомного архива.
1149 dev 709
 
1154 dev 710
sub create_tmp_list{
1149 dev 711
	my ($arc_block_level, $position1, $position2, $full_list_path) = @_;
712
	my ($tmp_list_path, $pos_counter);
713
 
714
    if ($arc_block_level == 0 && $position1 == 0 && $position2 eq ''){
715
	$tmp_list_path = $full_list_path;
716
    } else {
717
	$pos_counter = 0;
718
	$tmp_list_path = "$full_list_path.$arc_block_level";
719
	open(FULL_LIST, "<$full_list_path")|| die "Can't open full list $full_list_path\n";
720
	flock(FULL_LIST, 1);
721
	open(TMP_LIST, ">$tmp_list_path")|| die "Can't create temp list $tmp_list_path\n";
722
	flock(TMP_LIST, 2);
723
	while(<FULL_LIST>){
724
	    if (($pos_counter >= $position1) && ($pos_counter < $position2 || $position2 eq '')){
725
		print TMP_LIST $_;
726
	    }
727
	    $pos_counter++;
728
	}
729
	close(TMP_LIST);
730
	close(FULL_LIST);
731
    }
732
    return $tmp_list_path;
733
}
734
###############################################
735
###############################################
736
 
737
__END__
738
 
739
=head1 NAME
740
 
741
fsbackup - file system backup and synchronization utility. 
742
 
743
=head1 SYNOPSIS
744
 
745
    fsbackup.pl [options] <configuration file>
746
 
747
=head1 DESCRIPTION
748
 
749
C<fsbackup.pl> is a incremental backup creation utility. 
750
C<fsbackup.pl> support backup compression and encryption. Backup can be stored
751
on local file system and on remote host stored over SSH or FTP. Some addition 
752
scripts allow backups SQL tables from PostgreSQL and MySQL (C<pgsql_backup.sh> 
753
and C<mysql_backup.sh>)), save system configuration files and list of installed 
754
packages (C<sysbackup.sh>). 
755
Backuped with C<fsbackup.pl> files can be recovered by script C<fsrestore.sh>,
756
backuped with C<sysbackup.sh> system packeges can be reinstalled by C<sysrestore.sh>
757
 
758
=head1 OPTIONS
759
 
760
The following command-line options can be used with C<fsbackup.pl>:
761
 
762
=over
763
 
764
=item C<-n>
765
 
766
Create new backup without checking files in previously stored hash.
767
 
768
=item C<-f>
769
 
770
Create full backup, like as C<-n> option.
771
 
772
=item C<-h>
773
 
774
Only rebuild hash, no storing files in backup archive.
775
 
776
=item C<-c>
777
 
778
Clean incremental backup storage and create new full backup without checking
779
$cfg_increment_level config parameter.
780
 
781
=head1 ADDITION SCRIPTS
782
 
783
=item C<create_backup.sh>
784
 
785
Backup planner running from C<crontab>. For example: 
786
 
1154 dev 787
18 4 * * * /root/backup/fsbackup/create_backup.sh
1149 dev 788
 
789
=item C<install.pl>
790
 
791
Script to install fsbackup package and some required perl modules.
792
 
793
=item C<fsbackup.pl>
794
 
795
File system backup utility.
796
 
797
=item C<cfg_example>
798
 
799
Example of configuration file.
800
 
801
=item C<scripts/pgsql_backup.sh>
802
 
803
=item C<scripts/mysql_backup.sh>
804
 
805
Script for backup SQL tables from PostreSQL and MySQL.
806
 
807
=item C<scripts/sysbackup.sh>
808
 
809
Script for store system configuration files and information about installed
810
packages.
811
 
812
=item C<scripts/fsrestore.sh>
813
 
814
Script for restore files backuped by C<fsbackup.pl>.
815
 
816
=item C<scripts/sysrestore.sh>
817
 
818
Script for reinstall packages stored by C<sysbackup.sh>.
819
 
820
 
821
=head1 CONFIGURATION FILE
822
 
823
=item B<$cfg_backup_name> = 'test_host'
824
 
825
Name of backup, single word.
826
 
1154 dev 827
=item B<$cfg_cache_dir> = '/root/backup/fsbackup/cache'
1149 dev 828
 
829
Path of internal cache directory for local backup method.
830
 
831
=item B<$prog_md5sum> = 'md5sum -b'
832
 
833
=item B<$prog_tar> = 'tar'
834
 
835
=item B<$prog_ssh> = 'ssh'
836
 
837
=item B<$prog_rm> = 'rm'
838
 
839
=item B<$prog_gzip> = 'gzip'
840
 
841
=item B<$prog_pgp> = 'gpg'
842
 
843
Full path of some external program running from C<fsbackup.pl>.
844
B<$prog_gzip = ''> - not use compression, B<$prog_pgp = ''> - not use 
845
encryption.
846
 
847
=item B<$cfg_checksum> = 'timesize'
848
 
849
File checksum method: 
850
 
851
timesize - checksum of file attributes (default, best speed) 
852
 
853
md5      - checksum of file attributes + MD5 checksum of file content.
854
 
855
=item B<$cfg_backup_style> = 'backup'
856
 
857
Backup style:
858
 
859
backup - incremental backup (copy only new and changed files).
860
 
861
full_backup - full backup (copy all files).	
862
 
863
sync - file tree synchronization.
864
 
865
hash - hash creation without storing archive (spying for new or changed files).
866
 
867
=item B<$cfg_increment_level> = 7
868
 
869
Incremental level (after how many incremental copy make full refresh of backup)
870
 
871
=item B<$cfg_type> = 'remote_ssh'
872
 
873
Type of backup storage:
874
 
875
    local  - store backup on local file system.
876
    remote_ssh - store backup on remote host over SSH connection.
877
    remote_ftp - store backup on remote FTP server.
878
 
879
 
880
=item B<$cfg_remote_host> = 'backup-server.test.ru'
881
 
882
=item B<$cfg_remote_login> = 'backup_login'
883
 
884
=item B<$cfg_remote_path> = '/home/backup_login/backup'
885
 
886
Connection parameters for remote_ssh storage type.
887
 
888
=item B<$cfg_remote_password> = 'Test1234'
889
 
890
Password of remote login for remote_ftp storage type.
891
 
892
=item B<$cfg_local_path> = '/var/backup/'
893
 
894
Path of directory to store backup on local file system for local storage type.
895
 
896
=item B<$cfg_time_limit> = 0
897
 
898
Limit of file creation time in days. If not 0, don't backup files created or 
899
modified later then $cfg_time_limit (days).
900
 
901
=item B<$cfg_size_limit> = 0
902
 
903
Limit of maximum file size. If not 0, don't backup files witch size more then 
904
$cfg_time_limit kilobytes.
905
 
906
=item B<$cfg_root_path> = '/'
907
 
908
Root path for initial chdir.
909
 
910
=item B<$cfg_pgp_userid> = ''
911
 
912
Name of user in public key ring with public key will be used for PGP encryption.
913
Not use encryption if not set.
914
 
915
=item B<$cfg_verbose> = 3
916
 
917
Verbose level.
918
 
919
 
920
    1	- Output errors and warnings.
921
    2	- Output all the  available  data.
922
 
923
=item B<$cfg_save_old_backup> = 1
924
 
925
Save previous backup to OLD directory before rotation or before storing full backup.
926
 
927
 
928
    1 - save old backup.
929
 
930
=item B<$cfg_maximum_archive_size> = 0
931
 
932
Size of maximum size (in KiloBytes) of single unpacked archive file (0 - unlimited file size).
933
 
934
=item B<$cfg_stopdir_prune> = 0
935
 
936
Recursive review of the prohibited directories.
937
 
938
    1 - not use a recursive entrance to directory prohibited for backup (speed is increased, reduces flexibility of customization).
939
 
940
=item B<__DATA__> - list of backuped path and regexp mask.
941
 
942
    /dir[/file] - backup file or directory.
943
    !/dir[/file] - NOT include this file or directory to backup.
944
    # - ignore this line.
945
 
946
Mask:
947
 
948
    =~ - regexp mask for include file or directory to backup.
949
    f~ - regexp file mask for include file to backup.
950
    d~ - regexp directory mask for include directory to backup.
951
    =! - regexp mask for NOT include file or directory to backup.
952
    f! - regexp file mask for NOT include file to backup.
953
    d! - regexp directory mask for NOT include directory to backup.
954
 
955
 
956
Operation priority:
957
 
958
    1. =!
959
    2. f!
960
    3. f~
961
    4. d!
962
    5. =~
963
    6. d~
964
    7. !path
965
    8. path
966
 
967
 
968
=head1 COPYRIGHT
969
 
970
Copyright (c) 2001 by Maxim Chirkov <mc@tyumen.ru>
971
http://www.opennet.ru/dev/fsbackup/
972
 
973
=head1 BUGS
974
 
975
Look TODO file.
976
 
977
=head1 AUTHORS
978
 
979
Maxim Chirkov <mc@tyumen.ru>
980
 
981
=cut