Got rid of an unused variable in the rsyncsums perl script.
[rsync/rsync-patches.git] / checksum-xattr.diff
CommitLineData
502d2817
WD
1This patch is the start of storing/using checksum information from
2extended attribute values. The rsync code only reads the values
3at the moment. There is also a perl script that can create them.
4
5To use this patch, run these commands for a successful build:
6
7 patch -p1 <patches/checksum-xattr.diff
8 ./configure (optional if already run)
9 make
10
11--- old/flist.c
12+++ new/flist.c
13@@ -1193,7 +1193,8 @@ struct file_struct *make_file(const char
14 }
15 #endif
16
17- if (always_checksum && am_sender && S_ISREG(st.st_mode))
18+ if (always_checksum && am_sender && S_ISREG(st.st_mode)
19+ && !get_sum_xattr(thisname, &st, tmp_sum))
20 file_checksum(thisname, tmp_sum, st.st_size);
21
22 F_PATHNAME(file) = pathname;
23--- old/generator.c
24+++ new/generator.c
25@@ -627,7 +627,8 @@ int unchanged_file(char *fn, struct file
26 of the file time to determine whether to sync */
27 if (always_checksum > 0 && S_ISREG(st->st_mode)) {
28 char sum[MAX_DIGEST_LEN];
29- file_checksum(fn, sum, st->st_size);
30+ if (!get_sum_xattr(fn, st, sum))
31+ file_checksum(fn, sum, st->st_size);
32 return memcmp(sum, F_SUM(file), checksum_len) == 0;
33 }
34
35--- old/rsync.h
36+++ new/rsync.h
37@@ -1070,6 +1070,12 @@ isDigit(const char *ptr)
38 }
39
40 static inline int
41+isXDigit(const char *ptr)
42+{
43+ return isxdigit(*(unsigned char *)ptr);
44+}
45+
46+static inline int
47 isPrint(const char *ptr)
48 {
49 return isprint(*(unsigned char *)ptr);
50--- old/support/xsums
51+++ new/support/xsums
52@@ -0,0 +1,113 @@
53+#!/usr/bin/perl -w
54+use strict;
55+
56+use Getopt::Long;
57+use Cwd qw(abs_path cwd);
58+use Digest::MD4;
59+use Digest::MD5;
60+use File::ExtAttr ':all';
61+
62+our($recurse_opt, $help_opt);
63+our $verbosity = 0;
64+
65+&Getopt::Long::Configure('bundling');
66+&usage if !&GetOptions(
67+ 'recurse|r' => \$recurse_opt,
68+ 'verbose|v+' => \$verbosity,
69+ 'help|h' => \$help_opt,
70+) || $help_opt;
71+
72+my $start_dir = cwd();
73+
74+my @dirs = @ARGV;
75+@dirs = '.' unless @dirs;
76+foreach (@dirs) {
77+ $_ = abs_path($_);
78+}
79+
80+$| = 1;
81+
82+my $md4 = Digest::MD4->new;
83+my $md5 = Digest::MD5->new;
84+
85+while (@dirs) {
86+ my $dir = shift @dirs;
87+
88+ if (!chdir($dir)) {
89+ warn "Unable to chdir to $dir: $!\n";
90+ next;
91+ }
92+ if (!opendir(DP, '.')) {
93+ warn "Unable to opendir $dir: $!\n";
94+ next;
95+ }
96+
97+ if ($verbosity) {
98+ my $reldir = $dir;
99+ $reldir =~ s#^$start_dir(/|$)# $1 ? '' : '.' #eo;
100+ print "scanning $reldir\n";
101+ }
102+
103+ my @subdirs;
104+ while (defined(my $fn = readdir(DP))) {
105+ next if $fn =~ /^\.\.?$/ || -l $fn;
106+ if (-d _) {
107+ push(@subdirs, "$dir/$fn");
108+ next;
109+ }
110+ next unless -f _;
111+
112+ my($size,$mtime) = (stat(_))[7,9];
113+
114+ my $sum4 = getfattr($fn, 'rsync.%md4');
115+ my $sum5 = getfattr($fn, 'rsync.%md5');
116+
117+ foreach ($sum4, $sum5) {
118+ if (defined $_) {
119+ my($sum, $sz, $mt) = /^([0-9a-f]{32}) (\d+) (\d+)$/;
120+ if (!defined($mt) || $sz != $size || $mt != $mtime) {
121+ $_ = undef;
122+ } else {
123+ $_ = $sum;
124+ }
125+ }
126+ }
127+ if (!defined($sum4) || !defined($sum5)) {
128+ if (!open(IN, $fn)) {
129+ print STDERR "Unable to read $fn: $!\n";
130+ next;
131+ }
132+
133+ while (sysread(IN, $_, 64*1024)) {
134+ $md4->add($_);
135+ $md5->add($_);
136+ }
137+ close IN;
138+
139+ $sum4 = $md4->hexdigest;
140+ $sum5 = $md5->hexdigest;
141+ print " $sum4 $sum5" if $verbosity > 2;
142+ print " $fn\n" if $verbosity > 1;
143+
144+ setfattr($fn, 'rsync.%md4', "$sum4 $size $mtime");
145+ setfattr($fn, 'rsync.%md5', "$sum5 $size $mtime");
146+ #utime $mtime, $mtime, $fn; # Set mtime if it changes.
147+ }
148+ }
149+
150+ closedir DP;
151+
152+ unshift(@dirs, sort @subdirs) if $recurse_opt;
153+}
154+
155+sub usage
156+{
157+ die <<EOT;
158+Usage: rsyncsums [OPTIONS] [DIRS]
159+
160+Options:
161+ -r, --recurse Update checksums in subdirectories too.
162+ -v, --verbose Mention what we're doing. Repeat for more info.
163+ -h, --help Display this help message.
164+EOT
165+}
166--- old/xattrs.c
167+++ new/xattrs.c
168@@ -31,6 +31,8 @@ extern int am_generator;
169 extern int read_only;
170 extern int list_only;
171 extern int checksum_seed;
172+extern int checksum_len;
173+extern int protocol_version;
174
175 #define RSYNC_XAL_INITIAL 5
176 #define RSYNC_XAL_LIST_INITIAL 100
177@@ -64,6 +66,9 @@ extern int checksum_seed;
178 #define XSTAT_ATTR RSYNC_PREFIX "%stat"
179 #define XSTAT_LEN ((int)sizeof XSTAT_ATTR - 1)
180
181+#define MD4_ATTR RSYNC_PREFIX "%md4"
182+#define MD5_ATTR RSYNC_PREFIX "%md5"
183+
184 typedef struct {
185 char *datum, *name;
186 size_t datum_len, name_len;
187@@ -795,6 +800,71 @@ int set_xattr(const char *fname, const s
188 return rsync_xal_set(fname, lst + ndx, fnamecmp, sxp);
189 }
190
191+int get_sum_xattr(const char *fname, STRUCT_STAT *stp, char *sum)
192+{
193+ const char *mdattr = protocol_version >= 30
194+ ? MD5_ATTR : MD4_ATTR;
195+ char *cp, buf[256];
196+ OFF_T file_length;
197+ time_t mtime;
198+ int i, len;
199+
200+ len = sys_lgetxattr(fname, mdattr, buf, sizeof buf - 1);
201+ if (len >= (int)sizeof buf) {
202+ len = -1;
203+ errno = ERANGE;
204+ }
205+ if (len < 0) {
206+ if (errno == ENOTSUP || errno == ENOATTR)
207+ return 0;
208+ rsyserr(FERROR, errno, "failed to read xattr %s for %s",
209+ mdattr, full_fname(fname));
210+ return 0;
211+ }
212+ buf[len] = '\0';
213+
214+ for (i = 0, cp = buf; i < checksum_len*2; i++, cp++) {
215+ int x;
216+ if (isXDigit(cp)) {
217+ if (isDigit(cp))
218+ x = *cp - '0';
219+ else
220+ x = (*cp & 0xF) + 9;
221+ } else {
222+ cp = "";
223+ break;
224+ }
225+ if (i & 1)
226+ sum[i/2] |= x;
227+ else
228+ sum[i/2] = x << 4;
229+ }
230+ if (*cp++ != ' ')
231+ goto parse_error;
232+
233+ file_length = 0;
234+ while (isDigit(cp))
235+ file_length = file_length * 10 + *cp++ - '0';
236+ if (*cp++ != ' ')
237+ goto parse_error;
238+
239+ mtime = 0;
240+ while (isDigit(cp))
241+ mtime = mtime * 10 + *cp++ - '0';
242+ if (*cp)
243+ goto parse_error;
244+
245+ if (stp->st_size != file_length || stp->st_mtime != mtime)
246+ return 0;
247+
248+ return 1;
249+
250+ parse_error:
251+ rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
252+ mdattr, full_fname(fname), buf);
253+ exit_cleanup(RERR_FILEIO);
254+}
255+
256 int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
257 {
258 int mode, rdev_major, rdev_minor, uid, gid, len;