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>