Subversion Repositories general

Rev

Rev 1227 | Rev 1253 | 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 1228 2006-03-01 12:04:12Z 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
########################################
496
sub add_to_backup{
497
  my($file_name, $file_dir, $md5_checksum_stat, $checksum_stat);
1205 dev 498
  my($tmp, $stat_mode, $stat_uid, $stat_gid, $stat_size, $stat_mtime, $stat_time, $stat_flags);
1149 dev 499
 
500
  $file_name  = $_;
501
  $file_fullpath  = $File::Find::name;
502
  $file_dir  = $File::Find::dir;
503
  my $file_fullpath_esc = $file_fullpath;
504
  $file_fullpath_esc =~ s/\'/\'\\\'\'/g;
505
 
1153 dev 506
  # Создаем список директорий
1151 dev 507
  if ((-d $file_fullpath) && (! -l $file_fullpath)){
1149 dev 508
      if (check_path($file_dir, $file_name) == 1){
509
	if ($cfg_backup_style ne "hash"){
1228 dev 510
	    ($tmp, $tmp, $stat_mode, $tmp, $stat_uid, $stat_gid, $tmp, $stat_size, $tmp, $stat_mtime, $stat_time, $tmp, $tmp, $tmp, $tmp, $tmp, $stat_flags, $tmp) = lstat($file_fullpath);
511
	    $stat_mode = sprintf ("%04o", $stat_mode & 07777);
512
	    $file_fullpath_esc =~ s/^\/(.*)$/$1/;
513
	    my ($sec,$min,$hour,$mday,$mon,$year) = localtime($stat_time);
514
	    $stat_time = sprintf ("%4.4d%2.2d%2.2d%2.2d%2.2d.%2.2d",
515
		                  $year+1900,$mon+1,$mday,$hour,$min,$sec);
516
    	    print DIRS "mkdir -p '$file_fullpath_esc'\n";
517
    	    print DIRS "chmod $stat_mode '$file_fullpath_esc'\n";
518
    	    print DIRS "chown $stat_uid:$stat_gid '$file_fullpath_esc'\n";
519
    	    print DIRS "touch -t $stat_time '$file_fullpath_esc'\n";
520
            if($stat_flags) {
521
		print DIRS "chflags "
522
		    . ($stat_flags & UF_NODUMP    ? "nodump," : "")
523
		    . ($stat_flags & UF_IMMUTABLE ? "uchg,"   : "")
524
		    . ($stat_flags & UF_APPEND    ? "uappnd," : "")
525
		    . ($stat_flags & UF_OPAQUE    ? "opaque," : "")
526
		    . ($stat_flags & UF_NOUNLINK  ? "uunlnk," : "")
527
		    . ($stat_flags & SF_ARCHIVED  ? "arch,"   : "")
528
		    . ($stat_flags & SF_IMMUTABLE ? "schg,"   : "")
529
		    . ($stat_flags & SF_APPEND    ? "sappnd," : "")
530
		    . ($stat_flags & SF_NOUNLINK  ? "sunlnk," : "")
531
		    . " '$file_fullpath_esc'\n";
532
	    }
533
 
1205 dev 534
	    unless($stat_flags & UF_NODUMP) {
535
	        $cur_backup_size += int(length($file_fullpath)/100.0 + 1)*512;
536
	        if ($cfg_maximum_archive_size > 0 && $cur_backup_size + 10240 >= $cfg_maximum_archive_size){
537
	            my $old_val = $cur_backup_size - $stat_size - int(length($file_fullpath)/100.0 + 1)*512;
538
		    my $tmp_pos= $#volume_position+1;
539
	            print "Volume $tmp_pos Done. Size: $old_val\n" if ($cfg_verbose == &VERB_ALL);
540
		    $cur_backup_size = $stat_size + int(length($file_fullpath)/100.0 + 1)*512 + 1536;
541
	    	    push @volume_position, $list_lines_cnt;
542
	        }
543
	        $active_hash_new{$file_fullpath} = "D";
544
	        check_update($file_fullpath, "D", $file_fullpath, $stat_size);
1149 dev 545
	    }
1205 dev 546
	    else {
547
	        push @fs_notpath, "$file_dir/$file_name"; # if nodump - skip all subitems
548
                if ($cfg_stopdir_prune == 1){
549
                    $File::Find::prune = 1;
550
                    return;
551
	        }
552
	    }
1149 dev 553
	}
554
      } else {
555
          if ($cfg_stopdir_prune == 1){
556
              $File::Find::prune = 1;
557
              return;
558
	  }
559
      }
1205 dev 560
    # Работаем с файлами
561
    } elsif ((-f $file_fullpath) || (-l $file_fullpath)){
562
        if (check_path($file_dir, $file_name) == 1){
1227 dev 563
	    ($tmp, $tmp, $stat_mode, $tmp, $stat_uid, $stat_gid, $tmp, $stat_size, $tmp, $stat_mtime, $stat_time, $tmp, $tmp, $tmp, $tmp, $tmp, $stat_flags, $tmp) = lstat($file_fullpath);
1205 dev 564
	    unless($stat_flags & UF_NODUMP) {
565
	        $checksum_stat= md5_base64("$stat_mtime/$stat_size/$stat_mode/$stat_uid/$stat_gid");
566
	        # $file_fullpath_md5 = md5_base64($file_fullpath);
567
	        $file_fullpath_md5 = $file_fullpath;
568
	        if ($cfg_time_limit != 0 && $cur_time - $cfg_time_limit > $stat_mtime){
569
	            print "Time limit: $cur_time - $cfg_time_limit > $stat_mtime, file $file_fullpath ignored.\n" if ($cfg_verbose == &VERB_ALL);
570
	            next;
571
	        }
572
	        if ($cfg_size_limit != 0 && $cfg_size_limit < $stat_size){
573
	            print "Size limit: $cfg_size_limit < $stat_size, file $file_fullpath ignored.\n" if ($cfg_verbose == &VERB_ALL);
574
	            next;
575
	        }
1149 dev 576
 
1205 dev 577
	        if (($cfg_checksum eq "md5") && (! -l $file_fullpath)){
578
	            ($md5_checksum_stat, $tmp) = split(/\s+/, `$prog_md5sum '$file_fullpath_esc'`);
579
	            $active_hash_new{$file_fullpath_md5} = "$checksum_stat/$md5_checksum_stat";
580
	            check_update($file_fullpath, "$checksum_stat/$md5_checksum_stat", $file_fullpath_md5, $stat_size);
581
	        } else {
582
	            $active_hash_new{$file_fullpath} = $checksum_stat;
583
	            check_update($file_fullpath, $checksum_stat, $file_fullpath, $stat_size);
584
	        }
585
            }
586
        }
587
    }
1149 dev 588
}
589
 
590
###############################################
1153 dev 591
# Проверяем изменился ли файл или нет, если да апдейтим лог.
1149 dev 592
sub check_update{
593
     my ($file, $checksum, $filesum, $stat_size) = @_;
594
 
1154 dev 595
    if (($active_hash_last{$filesum} ne $checksum) && ($checksum ne "D")){
1149 dev 596
	if ($cfg_backup_style ne "hash"){
597
		$file =~ s/^\/(.*)$/$1/;
598
	        print LIST "$file\n";
599
 
1153 dev 600
	        # Обрабатываем случай разбиения гиганских архивов.
1149 dev 601
		if (-l "/$file"){
602
		    $stat_size = 0;
603
		}
604
	        $cur_backup_size += $stat_size + int(length($file)/100.0 + 1)*512;
605
#	  	print "$cur_backup_size:$stat_size:$file\n";
606
	        if ($cfg_maximum_archive_size > 0 && $cur_backup_size + 10240 >= $cfg_maximum_archive_size){
607
	        my $old_val = $cur_backup_size - $stat_size - int(length($file)/100.0 + 1)*512;
608
		my $tmp_pos= $#volume_position+1;
609
	        print "Volume $tmp_pos Done. Size: $old_val\n" if ($cfg_verbose == &VERB_ALL);
610
	        $cur_backup_size = $stat_size + int(length($file)/100.0 + 1)*512 + 1536;
611
	        push @volume_position, $list_lines_cnt;
612
	  }
613
 
614
	}
615
	$list_lines_cnt++;
616
    }
1154 dev 617
    if(($active_hash_last{$filesum} eq "D") && ($checksum ne "D")
618
      || ($active_hash_last{$filesum} ne "D") && ($checksum eq "D"))
619
    {
620
        # if old entry was a directory and now it's file or link or vice versa, leave it in hash 
621
        # to add it later to the delete list
1149 dev 622
    }
1154 dev 623
    else {
624
        delete $active_hash_last{$filesum};
625
        if (defined $dbobj_last){
626
	    $dbobj_last->del($filesum);
627
        }
628
    }
1149 dev 629
}
630
 
631
###############################################
1153 dev 632
# 0 - не добавлять файл
633
# 1 - добавть файл
1149 dev 634
 
635
sub check_path {
636
    my ($dir_name, $file_name) = @_;
637
    my ($item, $path);
638
 
639
    $path = "$dir_name/$file_name";
640
 
1151 dev 641
 
642
 
1149 dev 643
    foreach $item (@fs_notmask){
644
	if ($path =~ /$item/){
645
	    return 0;
646
	}
647
    }
648
 
649
    foreach $item (@fs_notfilemask){
650
	if ($file_name =~ /$item/){
651
	    return 0;
652
	}
653
    }
654
 
655
    foreach $item (@fs_filemask){
656
	if ($file_name =~ /$item/){
657
	    return 1;
658
	}
659
    }
660
 
661
    foreach $item (@fs_notdirmask){
662
	if ($dir_name =~ /$item/){
663
	    return 0;
664
	}
665
    }
666
 
667
 
668
    foreach $item (@fs_mask){
669
	if ($path =~ /$item/){
670
	    return 1;
671
	}
672
    }
673
 
674
    foreach $item (@fs_dirmask){
675
	if ($dir_name =~ /$item/){
676
	    return 1;
677
	}
678
    }
679
 
680
 
681
    foreach $item (@fs_notpath){
682
	if (($dir_name eq $item) || ($path eq $item) || ($dir_name =~ /^$item\//)){
683
	    return 0;
684
	}
685
    }
686
 
687
    return 1;
688
}
689
###############################################
1153 dev 690
# Устанавливаем соединение с удаленным сервером по FTP.
1149 dev 691
 
692
sub ftp_connect{
693
    if ( $cfg_type eq "remote_ftp"){
1151 dev 694
	$ftp = Net::FTP->new($cfg_remote_host, Timeout => 30, Debug => 0) || die "Can't connect to ftp server.\n";
1149 dev 695
	$ftp->login($cfg_remote_login, $cfg_remote_password) || die "Can't login to ftp server.\n";
696
        $ftp->cwd($cfg_remote_path) || die "Path $cfg_remote_path not found on ftp server.\n";
697
	$ftp->binary();    
698
    }
699
}
700
###############################################
1153 dev 701
# Содание списка файлов для помещения в определенный том многотомного архива.
1149 dev 702
 
1154 dev 703
sub create_tmp_list{
1149 dev 704
	my ($arc_block_level, $position1, $position2, $full_list_path) = @_;
705
	my ($tmp_list_path, $pos_counter);
706
 
707
    if ($arc_block_level == 0 && $position1 == 0 && $position2 eq ''){
708
	$tmp_list_path = $full_list_path;
709
    } else {
710
	$pos_counter = 0;
711
	$tmp_list_path = "$full_list_path.$arc_block_level";
712
	open(FULL_LIST, "<$full_list_path")|| die "Can't open full list $full_list_path\n";
713
	flock(FULL_LIST, 1);
714
	open(TMP_LIST, ">$tmp_list_path")|| die "Can't create temp list $tmp_list_path\n";
715
	flock(TMP_LIST, 2);
716
	while(<FULL_LIST>){
717
	    if (($pos_counter >= $position1) && ($pos_counter < $position2 || $position2 eq '')){
718
		print TMP_LIST $_;
719
	    }
720
	    $pos_counter++;
721
	}
722
	close(TMP_LIST);
723
	close(FULL_LIST);
724
    }
725
    return $tmp_list_path;
726
}
727
###############################################
728
###############################################
729
 
730
__END__
731
 
732
=head1 NAME
733
 
734
fsbackup - file system backup and synchronization utility. 
735
 
736
=head1 SYNOPSIS
737
 
738
    fsbackup.pl [options] <configuration file>
739
 
740
=head1 DESCRIPTION
741
 
742
C<fsbackup.pl> is a incremental backup creation utility. 
743
C<fsbackup.pl> support backup compression and encryption. Backup can be stored
744
on local file system and on remote host stored over SSH or FTP. Some addition 
745
scripts allow backups SQL tables from PostgreSQL and MySQL (C<pgsql_backup.sh> 
746
and C<mysql_backup.sh>)), save system configuration files and list of installed 
747
packages (C<sysbackup.sh>). 
748
Backuped with C<fsbackup.pl> files can be recovered by script C<fsrestore.sh>,
749
backuped with C<sysbackup.sh> system packeges can be reinstalled by C<sysrestore.sh>
750
 
751
=head1 OPTIONS
752
 
753
The following command-line options can be used with C<fsbackup.pl>:
754
 
755
=over
756
 
757
=item C<-n>
758
 
759
Create new backup without checking files in previously stored hash.
760
 
761
=item C<-f>
762
 
763
Create full backup, like as C<-n> option.
764
 
765
=item C<-h>
766
 
767
Only rebuild hash, no storing files in backup archive.
768
 
769
=item C<-c>
770
 
771
Clean incremental backup storage and create new full backup without checking
772
$cfg_increment_level config parameter.
773
 
774
=head1 ADDITION SCRIPTS
775
 
776
=item C<create_backup.sh>
777
 
778
Backup planner running from C<crontab>. For example: 
779
 
1154 dev 780
18 4 * * * /root/backup/fsbackup/create_backup.sh
1149 dev 781
 
782
=item C<install.pl>
783
 
784
Script to install fsbackup package and some required perl modules.
785
 
786
=item C<fsbackup.pl>
787
 
788
File system backup utility.
789
 
790
=item C<cfg_example>
791
 
792
Example of configuration file.
793
 
794
=item C<scripts/pgsql_backup.sh>
795
 
796
=item C<scripts/mysql_backup.sh>
797
 
798
Script for backup SQL tables from PostreSQL and MySQL.
799
 
800
=item C<scripts/sysbackup.sh>
801
 
802
Script for store system configuration files and information about installed
803
packages.
804
 
805
=item C<scripts/fsrestore.sh>
806
 
807
Script for restore files backuped by C<fsbackup.pl>.
808
 
809
=item C<scripts/sysrestore.sh>
810
 
811
Script for reinstall packages stored by C<sysbackup.sh>.
812
 
813
 
814
=head1 CONFIGURATION FILE
815
 
816
=item B<$cfg_backup_name> = 'test_host'
817
 
818
Name of backup, single word.
819
 
1154 dev 820
=item B<$cfg_cache_dir> = '/root/backup/fsbackup/cache'
1149 dev 821
 
822
Path of internal cache directory for local backup method.
823
 
824
=item B<$prog_md5sum> = 'md5sum -b'
825
 
826
=item B<$prog_tar> = 'tar'
827
 
828
=item B<$prog_ssh> = 'ssh'
829
 
830
=item B<$prog_rm> = 'rm'
831
 
832
=item B<$prog_gzip> = 'gzip'
833
 
834
=item B<$prog_pgp> = 'gpg'
835
 
836
Full path of some external program running from C<fsbackup.pl>.
837
B<$prog_gzip = ''> - not use compression, B<$prog_pgp = ''> - not use 
838
encryption.
839
 
840
=item B<$cfg_checksum> = 'timesize'
841
 
842
File checksum method: 
843
 
844
timesize - checksum of file attributes (default, best speed) 
845
 
846
md5      - checksum of file attributes + MD5 checksum of file content.
847
 
848
=item B<$cfg_backup_style> = 'backup'
849
 
850
Backup style:
851
 
852
backup - incremental backup (copy only new and changed files).
853
 
854
full_backup - full backup (copy all files).	
855
 
856
sync - file tree synchronization.
857
 
858
hash - hash creation without storing archive (spying for new or changed files).
859
 
860
=item B<$cfg_increment_level> = 7
861
 
862
Incremental level (after how many incremental copy make full refresh of backup)
863
 
864
=item B<$cfg_type> = 'remote_ssh'
865
 
866
Type of backup storage:
867
 
868
    local  - store backup on local file system.
869
    remote_ssh - store backup on remote host over SSH connection.
870
    remote_ftp - store backup on remote FTP server.
871
 
872
 
873
=item B<$cfg_remote_host> = 'backup-server.test.ru'
874
 
875
=item B<$cfg_remote_login> = 'backup_login'
876
 
877
=item B<$cfg_remote_path> = '/home/backup_login/backup'
878
 
879
Connection parameters for remote_ssh storage type.
880
 
881
=item B<$cfg_remote_password> = 'Test1234'
882
 
883
Password of remote login for remote_ftp storage type.
884
 
885
=item B<$cfg_local_path> = '/var/backup/'
886
 
887
Path of directory to store backup on local file system for local storage type.
888
 
889
=item B<$cfg_time_limit> = 0
890
 
891
Limit of file creation time in days. If not 0, don't backup files created or 
892
modified later then $cfg_time_limit (days).
893
 
894
=item B<$cfg_size_limit> = 0
895
 
896
Limit of maximum file size. If not 0, don't backup files witch size more then 
897
$cfg_time_limit kilobytes.
898
 
899
=item B<$cfg_root_path> = '/'
900
 
901
Root path for initial chdir.
902
 
903
=item B<$cfg_pgp_userid> = ''
904
 
905
Name of user in public key ring with public key will be used for PGP encryption.
906
Not use encryption if not set.
907
 
908
=item B<$cfg_verbose> = 3
909
 
910
Verbose level.
911
 
912
 
913
    1	- Output errors and warnings.
914
    2	- Output all the  available  data.
915
 
916
=item B<$cfg_save_old_backup> = 1
917
 
918
Save previous backup to OLD directory before rotation or before storing full backup.
919
 
920
 
921
    1 - save old backup.
922
 
923
=item B<$cfg_maximum_archive_size> = 0
924
 
925
Size of maximum size (in KiloBytes) of single unpacked archive file (0 - unlimited file size).
926
 
927
=item B<$cfg_stopdir_prune> = 0
928
 
929
Recursive review of the prohibited directories.
930
 
931
    1 - not use a recursive entrance to directory prohibited for backup (speed is increased, reduces flexibility of customization).
932
 
933
=item B<__DATA__> - list of backuped path and regexp mask.
934
 
935
    /dir[/file] - backup file or directory.
936
    !/dir[/file] - NOT include this file or directory to backup.
937
    # - ignore this line.
938
 
939
Mask:
940
 
941
    =~ - regexp mask for include file or directory to backup.
942
    f~ - regexp file mask for include file to backup.
943
    d~ - regexp directory mask for include directory to backup.
944
    =! - regexp mask for NOT include file or directory to backup.
945
    f! - regexp file mask for NOT include file to backup.
946
    d! - regexp directory mask for NOT include directory to backup.
947
 
948
 
949
Operation priority:
950
 
951
    1. =!
952
    2. f!
953
    3. f~
954
    4. d!
955
    5. =~
956
    6. d~
957
    7. !path
958
    8. path
959
 
960
 
961
=head1 COPYRIGHT
962
 
963
Copyright (c) 2001 by Maxim Chirkov <mc@tyumen.ru>
964
http://www.opennet.ru/dev/fsbackup/
965
 
966
=head1 BUGS
967
 
968
Look TODO file.
969
 
970
=head1 AUTHORS
971
 
972
Maxim Chirkov <mc@tyumen.ru>
973
 
974
=cut