Annotation of embedaddon/rsync/support/rsyncsums, revision 1.1.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>