X-Git-Url: https://mattmccutchen.net/rsync/rsync-patches.git/blobdiff_plain/502d2817abc0387ff9f5bdd144b5e122b16dfcbc..041359b073df4fe698ec80f2e0699a2585b2337b:/checksum-xattr.diff diff --git a/checksum-xattr.diff b/checksum-xattr.diff index 1713186..23febb9 100644 --- a/checksum-xattr.diff +++ b/checksum-xattr.diff @@ -8,21 +8,24 @@ To use this patch, run these commands for a successful build: ./configure (optional if already run) make ---- old/flist.c -+++ new/flist.c -@@ -1193,7 +1193,8 @@ struct file_struct *make_file(const char - } +based-on: 3b8f8192227b14e708bf535072485e50f4362270 +diff --git a/flist.c b/flist.c +--- a/flist.c ++++ b/flist.c +@@ -1309,7 +1309,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, #endif -- if (always_checksum && am_sender && S_ISREG(st.st_mode)) -+ if (always_checksum && am_sender && S_ISREG(st.st_mode) -+ && !get_sum_xattr(thisname, &st, tmp_sum)) - file_checksum(thisname, tmp_sum, st.st_size); - - F_PATHNAME(file) = pathname; ---- old/generator.c -+++ new/generator.c -@@ -627,7 +627,8 @@ int unchanged_file(char *fn, struct file + if (always_checksum && am_sender && S_ISREG(st.st_mode)) { +- file_checksum(thisname, tmp_sum, st.st_size); ++ if (!get_sum_xattr(thisname, &st, tmp_sum)) ++ file_checksum(thisname, tmp_sum, st.st_size); + if (sender_keeps_checksum) + extra_len += SUM_EXTRA_CNT * EXTRA_LEN; + } +diff --git a/generator.c b/generator.c +--- a/generator.c ++++ b/generator.c +@@ -532,7 +532,8 @@ int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st) of the file time to determine whether to sync */ if (always_checksum > 0 && S_ISREG(st->st_mode)) { char sum[MAX_DIGEST_LEN]; @@ -32,24 +35,11 @@ To use this patch, run these commands for a successful build: return memcmp(sum, F_SUM(file), checksum_len) == 0; } ---- old/rsync.h -+++ new/rsync.h -@@ -1070,6 +1070,12 @@ isDigit(const char *ptr) - } - - static inline int -+isXDigit(const char *ptr) -+{ -+ return isxdigit(*(unsigned char *)ptr); -+} -+ -+static inline int - isPrint(const char *ptr) - { - return isprint(*(unsigned char *)ptr); ---- old/support/xsums -+++ new/support/xsums -@@ -0,0 +1,113 @@ +diff --git a/support/xsums b/support/xsums +new file mode 100755 +--- /dev/null ++++ b/support/xsums +@@ -0,0 +1,204 @@ +#!/usr/bin/perl -w +use strict; + @@ -59,15 +49,15 @@ To use this patch, run these commands for a successful build: +use Digest::MD5; +use File::ExtAttr ':all'; + -+our($recurse_opt, $help_opt); -+our $verbosity = 0; -+ +&Getopt::Long::Configure('bundling'); +&usage if !&GetOptions( -+ 'recurse|r' => \$recurse_opt, -+ 'verbose|v+' => \$verbosity, -+ 'help|h' => \$help_opt, -+) || $help_opt; ++ 'recurse|r' => \( my $recurse_opt ), ++ 'list|l' => \( my $list_opt ), ++ 'check|c' => \( my $check_opt ), ++ 'verbose|v+' => \( my $verbosity = 0 ), ++ 'help|h' => \( my $help_opt ), ++); ++&usage if $help_opt; + +my $start_dir = cwd(); + @@ -79,6 +69,8 @@ To use this patch, run these commands for a successful build: + +$| = 1; + ++my $exit_code = 0; ++ +my $md4 = Digest::MD4->new; +my $md5 = Digest::MD5->new; + @@ -94,13 +86,13 @@ To use this patch, run these commands for a successful build: + next; + } + -+ if ($verbosity) { -+ my $reldir = $dir; -+ $reldir =~ s#^$start_dir(/|$)# $1 ? '' : '.' #eo; -+ print "scanning $reldir\n"; -+ } ++ my $reldir = $dir; ++ $reldir =~ s#^$start_dir(/|$)# $1 ? '' : '.' #eo; ++ print "$reldir ... " if $verbosity; + + my @subdirs; ++ my $d_cnt = 0; ++ my $need_newline = $verbosity; + while (defined(my $fn = readdir(DP))) { + next if $fn =~ /^\.\.?$/ || -l $fn; + if (-d _) { @@ -108,50 +100,137 @@ To use this patch, run these commands for a successful build: + next; + } + next unless -f _; ++ $d_cnt++; + -+ my($size,$mtime) = (stat(_))[7,9]; ++ my($size,$mtime,$ctime) = (stat(_))[7,9,10]; + -+ my $sum4 = getfattr($fn, 'rsync.%md4'); -+ my $sum5 = getfattr($fn, 'rsync.%md5'); ++ my $xsum4 = getfattr($fn, 'rsync.%md4'); ++ my $xsum5 = getfattr($fn, 'rsync.%md5'); + -+ foreach ($sum4, $sum5) { ++ my $sum_count = 0; ++ foreach ($xsum4, $xsum5) { + if (defined $_) { -+ my($sum, $sz, $mt) = /^([0-9a-f]{32}) (\d+) (\d+)$/; -+ if (!defined($mt) || $sz != $size || $mt != $mtime) { -+ $_ = undef; ++ if (length($_) == 24) { ++ my($sz,$mt,$sum) = unpack('V2a16', $_); ++ if ($sz != ($size & 0xFFFFFFFF) ++ || $mt != ($mtime & 0xFFFFFFFF)) { ++ $_ = undef; ++ } else { ++ $_ = $sum; ++ $sum_count++; ++ } + } else { -+ $_ = $sum; ++ $_ = undef; + } + } + } -+ if (!defined($sum4) || !defined($sum5)) { -+ if (!open(IN, $fn)) { -+ print STDERR "Unable to read $fn: $!\n"; ++ ++ if ($list_opt) { ++ if ($need_newline) { ++ print "\n"; ++ $need_newline = 0; ++ } ++ if (defined $xsum4) { ++ print ' ', unpack('H32', $xsum4); ++ } else { ++ print ' ' x (1 + 32); ++ } ++ if (defined $xsum5) { ++ print ' ', unpack('H32', $xsum5); ++ } else { ++ print ' ' x (1 + 32); ++ } ++ print $verbosity ? ' ' : " $reldir/"; ++ print $fn, "\n"; ++ next; ++ } ++ ++ if ($check_opt) { ++ if (!$sum_count) { ++ if ($need_newline) { ++ print "\n"; ++ $need_newline = 0; ++ } ++ print ' ' x (1 + 32 + 1 + 32) if $verbosity > 2; ++ print $verbosity ? ' ' : "$reldir/"; ++ print $fn, " MISSING\n"; + next; + } ++ } else { ++ next if $sum_count == 2; ++ print 'UPDATING' if $need_newline && $verbosity == 1; ++ } ++ ++ if ($need_newline && (!$check_opt || $verbosity > 1)) { ++ print "\n"; ++ $need_newline = 0; ++ } + ++ if (!open(IN, $fn)) { ++ print STDERR "Unable to read $fn: $!\n"; ++ next; ++ } ++ ++ my($sum4, $sum5); ++ while (1) { + while (sysread(IN, $_, 64*1024)) { + $md4->add($_); + $md5->add($_); + } -+ close IN; ++ $sum4 = $md4->digest; ++ $sum5 = $md5->digest; ++ print ' ', unpack('H32', $sum4), ' ', unpack('H32', $sum5) if $verbosity > 2; ++ print " $fn" if $verbosity > 1; ++ my($size2,$mtime2,$ctime2) = (stat(IN))[7,9,10]; ++ last if $size == $size2 && $mtime == $mtime2 && $ctime == $ctime2; ++ $size = $size2; ++ $mtime = $mtime2; ++ $ctime = $ctime2; ++ sysseek(IN, 0, 0); ++ print " REREADING\n" if $verbosity > 1; ++ } + -+ $sum4 = $md4->hexdigest; -+ $sum5 = $md5->hexdigest; -+ print " $sum4 $sum5" if $verbosity > 2; -+ print " $fn\n" if $verbosity > 1; ++ close IN; + -+ setfattr($fn, 'rsync.%md4', "$sum4 $size $mtime"); -+ setfattr($fn, 'rsync.%md5', "$sum5 $size $mtime"); ++ if ($check_opt) { ++ if ((!defined $xsum4 || $xsum4 eq $sum4) && (!defined $xsum5 || $xsum5 eq $sum5)) { ++ print " OK\n" if $verbosity > 1; ++ next; ++ } ++ if ($need_newline) { ++ print "\n"; ++ $need_newline = 0; ++ } ++ if ($verbosity < 2) { ++ print $verbosity ? ' ' : "$reldir/"; ++ print $fn; ++ } ++ print " FAILED\n"; ++ $exit_code = 1; ++ } else { ++ print "\n" if $verbosity > 1; ++ my $szmt = pack('V2', $size, $mtime); # 32-bits, may truncate ++ setfattr($fn, 'rsync.%md4', $szmt.$sum4); ++ setfattr($fn, 'rsync.%md5', $szmt.$sum5); + #utime $mtime, $mtime, $fn; # Set mtime if it changes. + } + } + ++ if ($need_newline) { ++ if ($d_cnt) { ++ print "ok\n"; ++ } else { ++ print "empty\n"; ++ } ++ } ++ + closedir DP; + + unshift(@dirs, sort @subdirs) if $recurse_opt; +} + ++exit $exit_code; ++ +sub usage +{ + die <= 30 + ? MD5_ATTR : MD4_ATTR; -+ char *cp, buf[256]; -+ OFF_T file_length; -+ time_t mtime; -+ int i, len; -+ -+ len = sys_lgetxattr(fname, mdattr, buf, sizeof buf - 1); -+ if (len >= (int)sizeof buf) { -+ len = -1; -+ errno = ERANGE; -+ } ++ char buf[256]; ++ uint32 file_length, mtime; ++ int len; ++ ++ len = sys_lgetxattr(fname, mdattr, buf, sizeof buf); + if (len < 0) { + if (errno == ENOTSUP || errno == ENOATTR) + return 0; -+ rsyserr(FERROR, errno, "failed to read xattr %s for %s", ++ rsyserr(FERROR_XFER, errno, "failed to read xattr %s for %s", + mdattr, full_fname(fname)); + return 0; + } -+ buf[len] = '\0'; -+ -+ for (i = 0, cp = buf; i < checksum_len*2; i++, cp++) { -+ int x; -+ if (isXDigit(cp)) { -+ if (isDigit(cp)) -+ x = *cp - '0'; -+ else -+ x = (*cp & 0xF) + 9; -+ } else { -+ cp = ""; -+ break; -+ } -+ if (i & 1) -+ sum[i/2] |= x; -+ else -+ sum[i/2] = x << 4; ++ if (len != 4 + 4 + checksum_len) { ++ rprintf(FERROR, "Corrupt %s xattr attached to %s -- skipping\n", ++ mdattr, full_fname(fname)); ++ return 0; + } -+ if (*cp++ != ' ') -+ goto parse_error; -+ -+ file_length = 0; -+ while (isDigit(cp)) -+ file_length = file_length * 10 + *cp++ - '0'; -+ if (*cp++ != ' ') -+ goto parse_error; -+ -+ mtime = 0; -+ while (isDigit(cp)) -+ mtime = mtime * 10 + *cp++ - '0'; -+ if (*cp) -+ goto parse_error; -+ -+ if (stp->st_size != file_length || stp->st_mtime != mtime) ++ ++ file_length = IVAL(buf, 0); /* 32-bit values -- trunctions are OK */ ++ mtime = IVAL(buf, 4); ++ ++ if ((uint32)stp->st_size != file_length || (uint32)stp->st_mtime != mtime) + return 0; + -+ return 1; ++ memcpy(sum, buf + 8, checksum_len); + -+ parse_error: -+ rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n", -+ mdattr, full_fname(fname), buf); -+ exit_cleanup(RERR_FILEIO); ++ return 1; +} + int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)