Annotation of embedaddon/rsync/support/rsyncsums, revision 1.1

1.1     ! misho       1: #!/usr/bin/perl -w
        !             2: use strict;
        !             3: 
        !             4: use Getopt::Long;
        !             5: use Cwd qw(abs_path cwd);
        !             6: use Digest::MD4;
        !             7: use Digest::MD5;
        !             8: 
        !             9: our $SUMS_FILE = '.rsyncsums';
        !            10: 
        !            11: &Getopt::Long::Configure('bundling');
        !            12: &usage if !&GetOptions(
        !            13:     'recurse|r' => \( my $recurse_opt ),
        !            14:     'mode|m=s' => \( my $cmp_mode = 'strict' ),
        !            15:     'check|c' => \( my $check_opt ),
        !            16:     'verbose|v+' => \( my $verbosity = 0 ),
        !            17:     'help|h' => \( my $help_opt ),
        !            18: );
        !            19: &usage if $help_opt || $cmp_mode !~ /^(lax|strict)$/;
        !            20: 
        !            21: my $ignore_ctime_and_inode = $cmp_mode eq 'lax' ? 0 : 1;
        !            22: 
        !            23: my $start_dir = cwd();
        !            24: 
        !            25: my @dirs = @ARGV;
        !            26: @dirs = '.' unless @dirs;
        !            27: foreach (@dirs) {
        !            28:     $_ = abs_path($_);
        !            29: }
        !            30: 
        !            31: $| = 1;
        !            32: 
        !            33: my $exit_code = 0;
        !            34: 
        !            35: my $md4 = Digest::MD4->new;
        !            36: my $md5 = Digest::MD5->new;
        !            37: 
        !            38: while (@dirs) {
        !            39:     my $dir = shift @dirs;
        !            40: 
        !            41:     if (!chdir($dir)) {
        !            42:        warn "Unable to chdir to $dir: $!\n";
        !            43:        next;
        !            44:     }
        !            45:     if (!opendir(DP, '.')) {
        !            46:        warn "Unable to opendir $dir: $!\n";
        !            47:        next;
        !            48:     }
        !            49: 
        !            50:     my $reldir = $dir;
        !            51:     $reldir =~ s#^$start_dir(/|$)# $1 ? '' : '.' #eo;
        !            52:     if ($verbosity) {
        !            53:        print "$reldir ... ";
        !            54:        print "\n" if $check_opt;
        !            55:     }
        !            56: 
        !            57:     my %cache;
        !            58:     my $f_cnt = 0;
        !            59:     if (open(FP, '<', $SUMS_FILE)) {
        !            60:        while (<FP>) {
        !            61:            chomp;
        !            62:            my($sum4, $sum5, $size, $mtime, $ctime, $inode, $fn) = split(' ', $_, 7);
        !            63:            $cache{$fn} = [ 0, $sum4, $sum5, $size, $mtime, $ctime & 0xFFFFFFFF, $inode & 0xFFFFFFFF ];
        !            64:            $f_cnt++;
        !            65:        }
        !            66:        close FP;
        !            67:     }
        !            68: 
        !            69:     my @subdirs;
        !            70:     my $d_cnt = 0;
        !            71:     my $update_cnt = 0;
        !            72:     while (defined(my $fn = readdir(DP))) {
        !            73:        next if $fn =~ /^\.\.?$/ || $fn =~ /^\Q$SUMS_FILE\E$/o || -l $fn;
        !            74:        if (-d _) {
        !            75:            push(@subdirs, "$dir/$fn") unless $fn =~ /^(CVS|\.svn|\.git|\.bzr)$/;
        !            76:            next;
        !            77:        }
        !            78:        next unless -f _;
        !            79: 
        !            80:        my($size,$mtime,$ctime,$inode) = (stat(_))[7,9,10,1];
        !            81:        $ctime &= 0xFFFFFFFF;
        !            82:        $inode &= 0xFFFFFFFF;
        !            83:        my $ref = $cache{$fn};
        !            84:        $d_cnt++;
        !            85: 
        !            86:        if (!$check_opt) {
        !            87:            if (defined $ref) {
        !            88:                $$ref[0] = 1;
        !            89:                if ($$ref[3] == $size
        !            90:                 && $$ref[4] == $mtime
        !            91:                 && ($ignore_ctime_and_inode || ($$ref[5] == $ctime && $$ref[6] == $inode))
        !            92:                 && $$ref[1] !~ /=/ && $$ref[2] !~ /=/) {
        !            93:                    next;
        !            94:                }
        !            95:            }
        !            96:            if (!$update_cnt++) {
        !            97:                print "UPDATING\n" if $verbosity;
        !            98:            }
        !            99:        }
        !           100: 
        !           101:        if (!open(IN, $fn)) {
        !           102:            print STDERR "Unable to read $fn: $!\n";
        !           103:            if (defined $ref) {
        !           104:                delete $cache{$fn};
        !           105:                $f_cnt--;
        !           106:            }
        !           107:            next;
        !           108:        }
        !           109: 
        !           110:        my($sum4, $sum5);
        !           111:        while (1) {
        !           112:            while (sysread(IN, $_, 64*1024)) {
        !           113:                $md4->add($_);
        !           114:                $md5->add($_);
        !           115:            }
        !           116:            $sum4 = $md4->hexdigest;
        !           117:            $sum5 = $md5->hexdigest;
        !           118:            print " $sum4 $sum5" if $verbosity > 2;
        !           119:            print " $fn" if $verbosity > 1;
        !           120:            my($size2,$mtime2,$ctime2,$inode2) = (stat(IN))[7,9,10,1];
        !           121:            $ctime2 &= 0xFFFFFFFF;
        !           122:            $inode2 &= 0xFFFFFFFF;
        !           123:            last if $size == $size2 && $mtime == $mtime2
        !           124:             && ($ignore_ctime_and_inode || ($ctime == $ctime2 && $inode == $inode2));
        !           125:            $size = $size2;
        !           126:            $mtime = $mtime2;
        !           127:            $ctime = $ctime2;
        !           128:            $inode = $inode2;
        !           129:            sysseek(IN, 0, 0);
        !           130:            print " REREADING\n" if $verbosity > 1;
        !           131:        }
        !           132: 
        !           133:        close IN;
        !           134: 
        !           135:        if ($check_opt) {
        !           136:            my $dif;
        !           137:            if (!defined $ref) {
        !           138:                $dif = 'MISSING';
        !           139:            } elsif ($sum4 ne $$ref[1] || $sum5 ne $$ref[2]) {
        !           140:                $dif = 'FAILED';
        !           141:            } else {
        !           142:                print " OK\n" if $verbosity > 1;
        !           143:                next;
        !           144:            }
        !           145:            if ($verbosity < 2) {
        !           146:                print $verbosity ? ' ' : "$reldir/";
        !           147:                print $fn;
        !           148:            }
        !           149:            print " $dif\n";
        !           150:            $exit_code = 1;
        !           151:        } else {
        !           152:            print "\n" if $verbosity > 1;
        !           153:            $cache{$fn} = [ 1, $sum4, $sum5, $size, $mtime, $ctime, $inode ];
        !           154:        }
        !           155:     }
        !           156: 
        !           157:     closedir DP;
        !           158: 
        !           159:     unshift(@dirs, sort @subdirs) if $recurse_opt;
        !           160: 
        !           161:     if ($check_opt) {
        !           162:        ;
        !           163:     } elsif ($d_cnt == 0) {
        !           164:        if ($f_cnt) {
        !           165:            print "(removed $SUMS_FILE) " if $verbosity;
        !           166:            unlink($SUMS_FILE);
        !           167:        }
        !           168:        print "empty\n" if $verbosity;
        !           169:     } elsif ($update_cnt || $d_cnt != $f_cnt) {
        !           170:        print "UPDATING\n" if $verbosity && !$update_cnt;
        !           171:        open(FP, '>', $SUMS_FILE) or die "Unable to write $dir/$SUMS_FILE: $!\n";
        !           172: 
        !           173:        foreach my $fn (sort keys %cache) {
        !           174:            my $ref = $cache{$fn};
        !           175:            my($found, $sum4, $sum5, $size, $mtime, $ctime, $inode) = @$ref;
        !           176:            next unless $found;
        !           177:            printf FP '%s %s %10d %10d %10d %10d %s' . "\n", $sum4, $sum5, $size, $mtime, $ctime, $inode, $fn;
        !           178:        }
        !           179:        close FP;
        !           180:     } else {
        !           181:        print "ok\n" if $verbosity;
        !           182:     }
        !           183: }
        !           184: 
        !           185: exit $exit_code;
        !           186: 
        !           187: sub usage
        !           188: {
        !           189:     die <<EOT;
        !           190: Usage: rsyncsums [OPTIONS] [DIRS]
        !           191: 
        !           192: Options:
        !           193:  -r, --recurse     Update $SUMS_FILE files in subdirectories too.
        !           194:  -m, --mode=MODE   Compare entries in either "lax" or "strict" mode.  Using
        !           195:                    "lax" compares size and mtime, while "strict" additionally
        !           196:                    compares ctime and inode.  Default:  strict.
        !           197:  -c, --check       Check if the checksums are right (doesn't update).
        !           198:  -v, --verbose     Mention what we're doing.  Repeat for more info.
        !           199:  -h, --help        Display this help message.
        !           200: EOT
        !           201: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>