这个脚本的基本原理是创建原目录结构的两个拷贝。第一个拷贝作为参照拷贝,其中包含目录结构的精确副本。这样,当再次同步目录时,就可以像一般情况一样比较源和目标文件并判断出差异。在 rsync 命令中使用 --itemize-changes 选项,rsync 就会创建一个参照列表,其中列出在同步期间每个文件所发生的情况。输出详细说明文件是否已经修改过(或新建),或文件是否已经删除。清单 3 中给出一个示例。
清单 3. rsync 生成的修改记录
.d..t...... t1/a/
*deleting t1/a/3
.d..t...... t1/b/
>f.st...... t1/b/1
>f+++++++++ t1/b/6
以 .d. 开头的行表示新目录或目录修改。*deleting 行表示文件已经从源目录中删除。>f 行表示文件已经修改过或是新建的文件(>f++++++++)。
通过解析这个输出文件,可以判断出源目录和目标参照目录之间的差异。判断出差异之后,可以在第三个目录中创建原文件的加密版本。通过使用修改记录,只加密(或删除)在上一次同步操作之后修改过的文件。不能使用目录的加密版本直接执行同步,因为文件的加密版本总是与源文件不一样。
完整的脚本见清单 4。
清单 4. 完整脚本
#!/usr/bin/perl
use warnings;
use strict;
use File::Basename;
use File::Path;
my $source = shift;
my $dest = shift;
my $encdest = shift;
if (!defined($source) || !defined($dest) || !defined($encdest))
{
print "Error: Not enough arguments!n";
print "Usage: $0 source destination encrypteddestn";
exit(1);
}
print STDERR "Running rsync between $source and $dest ($encdest)n";
system("rsync --delete --recursive --times -og --links --perms " .
"--hard-links --itemize-changes $source $dest " .
">/tmp/$$.rsynclog 2>&1");
open(DATA,"/tmp/$$.rsynclog") or dIE "Couldn't open the rsynclogn";
my @changedfiles;
my @delfiles;
while(<DATA>)
{
next if (m/sending incremental file list/);
chomp;
last if (length($_) == 0);
my ($changes,$filename) = split;
push @changedfiles,$filename if ($changes =~ m/^>f/);
push @delfiles,$filename if ($changes =~ m/^*del/);
}
close(DATA);
my $counter = 0;
foreach my $file (@changedfiles)
{
if (-f "$dest/$file")
{
my $sourcename = encode_filename("$dest/$file");
my $destname = encode_filename("$encdest/$file");
my $dirname = dirname("$encdest/$file");
mkpath($dirname);
system(sprintf('cat "%s" |openssl enc -des3 ' .
'-pass file:/var/lib/passphrase -a >"%s"',
$sourcename,$destname));
$counter++;
}
}
my $delcounter = 0;
foreach my $file (@delfiles)
{
unlink("$encdest/$file");
$delcounter++;
}
print STDERR "Finished (changed: $counter, deleted: $delcounter)n";
unlink("/tmp/$$.rsynclog");
sub encode_filename
{
my ($filename) = @_;
$filename =~ s/ / /g;
$filename =~ s/'/'/g;
$filename =~ s/"/"/g;
$filename =~ s/(/(/g;
$filename =~ s/)/)/g;
$filename =~ s/&/&/g;
$filename =~ s/#/#/g;
return($filename);
}
标签: