Subversion Repositories general

Rev

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