5e3b1a385d654e7dfc96477c8cd8aeff1d9aea62
[rsync-patches.git] / log-checksum.diff
1 This patch to rsync adds a %C log escape that expands to the sender's
2 post-transfer checksum of a file for protocol 30 or above.  This way, if
3 you need the MD5 checksums of transferred files, you can have rsync log
4 them instead of spending extra processor time on a separate command to
5 compute them.
6
7 -- Matt McCutchen <hashproduct@gmail.com>
8
9 --- old/log.c
10 +++ new/log.c
11 @@ -55,6 +55,9 @@ extern char curr_dir[];
12  extern char *module_dir;
13  extern unsigned int module_dirlen;
14  
15 +extern char sender_file_sum[MAX_DIGEST_LEN];
16 +extern int file_sum_len;
17 +
18  static int log_initialised;
19  static int logfile_was_closed;
20  static FILE *logfile_fp;
21 @@ -610,6 +613,19 @@ static void log_formatted(enum logcode c
22                         snprintf(buf2, sizeof buf2, fmt, (double)b);
23                         n = buf2;
24                         break;
25 +               case 'C':
26 +                       if (iflags & ITEM_TRANSFER && protocol_version >= 30) {
27 +                               int i;
28 +                               for (i = 0; i < file_sum_len; i++)
29 +                                       snprintf(buf2 + i*2, 3, "%02x", (int)CVAL(sender_file_sum,i));
30 +                       } else {
31 +                               int i;
32 +                               for (i = 0; i < file_sum_len*2; i++)
33 +                                       buf2[i] = '?';
34 +                               buf2[i] = '\0';
35 +                       }
36 +                       n = buf2;
37 +                       break;
38                 case 'i':
39                         if (iflags & ITEM_DELETED) {
40                                 n = "*deleting";
41 --- old/match.c
42 +++ new/match.c
43 @@ -286,6 +286,10 @@ static void hash_search(int f,struct sum
44         map_ptr(buf, len-1, 1);
45  }
46  
47 +/* Global variables to make the sender's checksum of a transferred file
48 + * available to the code for log escape %C. */
49 +char sender_file_sum[MAX_DIGEST_LEN];
50 +int file_sum_len = MD5_DIGEST_LEN;
51  
52  /**
53   * Scan through a origin file, looking for sections that match
54 @@ -303,9 +307,6 @@ static void hash_search(int f,struct sum
55   **/
56  void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len)
57  {
58 -       char file_sum[MAX_DIGEST_LEN];
59 -       int sum_len;
60 -
61         last_match = 0;
62         false_alarms = 0;
63         hash_hits = 0;
64 @@ -353,14 +354,26 @@ void match_sums(int f, struct sum_struct
65                 matched(f, s, buf, len, -1);
66         }
67  
68 -       sum_len = sum_end(file_sum);
69 -       /* If we had a read error, send a bad checksum. */
70 -       if (buf && buf->status != 0)
71 -               file_sum[0]++;
72 +       file_sum_len = sum_end(sender_file_sum);
73 +
74 +       /* If we had a read error, send a bad checksum.  We use all bits
75 +        * off or all bits on so that a user logging checksums with %C
76 +        * can recognize a bad checksum. */
77 +       if (buf && buf->status != 0) {
78 +               int i;
79 +               for (i = 0; i < file_sum_len; i++) {
80 +                       if (sender_file_sum[i])
81 +                               break;
82 +               }
83 +               if (i < file_sum_len)
84 +                       memset(sender_file_sum, 0, file_sum_len);
85 +               else
86 +                       memset(sender_file_sum, 0xFF, file_sum_len);
87 +       }
88  
89         if (verbose > 2)
90                 rprintf(FINFO,"sending file_sum\n");
91 -       write_buf(f, file_sum, sum_len);
92 +       write_buf(f, sender_file_sum, file_sum_len);
93  
94         if (verbose > 2)
95                 rprintf(FINFO, "false_alarms=%d hash_hits=%d matches=%d\n",
96 --- old/options.c
97 +++ new/options.c
98 @@ -1439,7 +1439,8 @@ int parse_arguments(int *argc, const cha
99                 else if (log_format_has(stdout_format, 'i'))
100                         stdout_format_has_i = itemize_changes | 1;
101                 if (!log_format_has(stdout_format, 'b')
102 -                && !log_format_has(stdout_format, 'c'))
103 +                && !log_format_has(stdout_format, 'c')
104 +                && !log_format_has(stdout_format, 'C'))
105                         log_before_transfer = !am_server;
106         } else if (itemize_changes) {
107                 stdout_format = "%i %n%L";
108 --- old/receiver.c
109 +++ new/receiver.c
110 @@ -62,6 +62,9 @@ static int phase = 0, redoing = 0;
111  /* We're either updating the basis file or an identical copy: */
112  static int updating_basis;
113  
114 +extern char sender_file_sum[MAX_DIGEST_LEN];
115 +extern int file_sum_len;
116 +
117  /*
118   * get_tmpname() - create a tmp filename for a given filename
119   *
120 @@ -128,10 +131,9 @@ static int receive_data(int f_in, char *
121                         const char *fname, int fd, OFF_T total_size)
122  {
123         static char file_sum1[MAX_DIGEST_LEN];
124 -       static char file_sum2[MAX_DIGEST_LEN];
125         struct map_struct *mapbuf;
126         struct sum_struct sum;
127 -       int32 len, sum_len;
128 +       int32 len;
129         OFF_T offset = 0;
130         OFF_T offset2;
131         char *data;
132 @@ -261,15 +263,15 @@ static int receive_data(int f_in, char *
133                 exit_cleanup(RERR_FILEIO);
134         }
135  
136 -       sum_len = sum_end(file_sum1);
137 +       file_sum_len = sum_end(file_sum1);
138  
139         if (mapbuf)
140                 unmap_file(mapbuf);
141  
142 -       read_buf(f_in, file_sum2, sum_len);
143 +       read_buf(f_in, sender_file_sum, file_sum_len);
144         if (verbose > 2)
145                 rprintf(FINFO,"got file_sum\n");
146 -       if (fd != -1 && memcmp(file_sum1, file_sum2, sum_len) != 0)
147 +       if (fd != -1 && memcmp(file_sum1, sender_file_sum, file_sum_len) != 0)
148                 return 0;
149         return 1;
150  }
151 --- old/rsync.yo
152 +++ new/rsync.yo
153 @@ -1970,7 +1970,7 @@ by the server and defaults to the curren
154  is used to set a specific checksum seed, which is useful for
155  applications that want repeatable block and file checksums, or
156  in the case where the user wants a more random checksum seed.
157 -Note that setting NUM to 0 causes rsync to use the default of code(time())
158 +Setting NUM to 0 causes rsync to use the default of code(time())
159  for checksum seed.
160  enddit()
161  
162 --- old/rsyncd.conf.yo
163 +++ new/rsyncd.conf.yo
164 @@ -421,7 +421,8 @@ quote(itemization(
165    it() %a the remote IP address
166    it() %b the number of bytes actually transferred
167    it() %B the permission bits of the file (e.g. rwxrwxrwt)
168 -  it() %c the checksum bytes received for this file (only when sending)
169 +  it() %c the total size of the block checksums received for the basis file (only when sending)
170 +  it() %C the full-file MD5 checksum of a transferred file (only for protocol 30 or above).
171    it() %f the filename (long form on sender; no trailing "/")
172    it() %G the gid of the file (decimal) or "DEFAULT"
173    it() %h the remote host name