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>