Fix issues with unchanged_attrs() for symlinks.
[rsync/rsync.git] / checksum.c
1 /*
2  * Routines to support checksumming of bytes.
3  *
4  * Copyright (C) 1996 Andrew Tridgell
5  * Copyright (C) 1996 Paul Mackerras
6  * Copyright (C) 2004-2009 Wayne Davison
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, visit the http://fsf.org website.
20  */
21
22 #include "rsync.h"
23
24 extern int checksum_seed;
25 extern int protocol_version;
26
27 /*
28   a simple 32 bit checksum that can be upadted from either end
29   (inspired by Mark Adler's Adler-32 checksum)
30   */
31 uint32 get_checksum1(char *buf1, int32 len)
32 {
33     int32 i;
34     uint32 s1, s2;
35     schar *buf = (schar *)buf1;
36
37     s1 = s2 = 0;
38     for (i = 0; i < (len-4); i+=4) {
39         s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] +
40           10*CHAR_OFFSET;
41         s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET);
42     }
43     for (; i < len; i++) {
44         s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
45     }
46     return (s1 & 0xffff) + (s2 << 16);
47 }
48
49
50 void get_checksum2(char *buf, int32 len, char *sum)
51 {
52         md_context m;
53
54         if (protocol_version >= 30) {
55                 uchar seedbuf[4];
56                 md5_begin(&m);
57                 md5_update(&m, (uchar *)buf, len);
58                 if (checksum_seed) {
59                         SIVALu(seedbuf, 0, checksum_seed);
60                         md5_update(&m, seedbuf, 4);
61                 }
62                 md5_result(&m, (uchar *)sum);
63         } else {
64                 int32 i;
65                 static char *buf1;
66                 static int32 len1;
67
68                 mdfour_begin(&m);
69
70                 if (len > len1) {
71                         if (buf1)
72                                 free(buf1);
73                         buf1 = new_array(char, len+4);
74                         len1 = len;
75                         if (!buf1)
76                                 out_of_memory("get_checksum2");
77                 }
78
79                 memcpy(buf1, buf, len);
80                 if (checksum_seed) {
81                         SIVAL(buf1,len,checksum_seed);
82                         len += 4;
83                 }
84
85                 for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
86                         mdfour_update(&m, (uchar *)(buf1+i), CSUM_CHUNK);
87
88                 /*
89                  * Prior to version 27 an incorrect MD4 checksum was computed
90                  * by failing to call mdfour_tail() for block sizes that
91                  * are multiples of 64.  This is fixed by calling mdfour_update()
92                  * even when there are no more bytes.
93                  */
94                 if (len - i > 0 || protocol_version >= 27)
95                         mdfour_update(&m, (uchar *)(buf1+i), len-i);
96
97                 mdfour_result(&m, (uchar *)sum);
98         }
99 }
100
101 void file_checksum(char *fname, char *sum, OFF_T size)
102 {
103         struct map_struct *buf;
104         OFF_T i, len = size;
105         md_context m;
106         int32 remainder;
107         int fd;
108
109         memset(sum, 0, MAX_DIGEST_LEN);
110
111         fd = do_open(fname, O_RDONLY, 0);
112         if (fd == -1)
113                 return;
114
115         buf = map_file(fd, size, MAX_MAP_SIZE, CSUM_CHUNK);
116
117         if (protocol_version >= 30) {
118                 md5_begin(&m);
119
120                 for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
121                         md5_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
122                                    CSUM_CHUNK);
123                 }
124
125                 remainder = (int32)(len - i);
126                 if (remainder > 0)
127                         md5_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
128
129                 md5_result(&m, (uchar *)sum);
130         } else {
131                 mdfour_begin(&m);
132
133                 for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
134                         mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
135                                       CSUM_CHUNK);
136                 }
137
138                 /* Prior to version 27 an incorrect MD4 checksum was computed
139                  * by failing to call mdfour_tail() for block sizes that
140                  * are multiples of 64.  This is fixed by calling mdfour_update()
141                  * even when there are no more bytes. */
142                 remainder = (int32)(len - i);
143                 if (remainder > 0 || protocol_version >= 27)
144                         mdfour_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
145
146                 mdfour_result(&m, (uchar *)sum);
147         }
148
149         close(fd);
150         unmap_file(buf);
151 }
152
153 static int32 sumresidue;
154 static md_context md;
155
156 void sum_init(int seed)
157 {
158         char s[4];
159
160         if (protocol_version >= 30)
161                 md5_begin(&md);
162         else {
163                 mdfour_begin(&md);
164                 sumresidue = 0;
165                 SIVAL(s, 0, seed);
166                 sum_update(s, 4);
167         }
168 }
169
170 /**
171  * Feed data into an MD4 accumulator, md.  The results may be
172  * retrieved using sum_end().  md is used for different purposes at
173  * different points during execution.
174  *
175  * @todo Perhaps get rid of md and just pass in the address each time.
176  * Very slightly clearer and slower.
177  **/
178 void sum_update(const char *p, int32 len)
179 {
180         if (protocol_version >= 30) {
181                 md5_update(&md, (uchar *)p, len);
182                 return;
183         }
184
185         if (len + sumresidue < CSUM_CHUNK) {
186                 memcpy(md.buffer + sumresidue, p, len);
187                 sumresidue += len;
188                 return;
189         }
190
191         if (sumresidue) {
192                 int32 i = CSUM_CHUNK - sumresidue;
193                 memcpy(md.buffer + sumresidue, p, i);
194                 mdfour_update(&md, (uchar *)md.buffer, CSUM_CHUNK);
195                 len -= i;
196                 p += i;
197         }
198
199         while (len >= CSUM_CHUNK) {
200                 mdfour_update(&md, (uchar *)p, CSUM_CHUNK);
201                 len -= CSUM_CHUNK;
202                 p += CSUM_CHUNK;
203         }
204
205         sumresidue = len;
206         if (sumresidue)
207                 memcpy(md.buffer, p, sumresidue);
208 }
209
210 int sum_end(char *sum)
211 {
212         if (protocol_version >= 30) {
213                 md5_result(&md, (uchar *)sum);
214                 return MD5_DIGEST_LEN;
215         }
216
217         if (sumresidue || protocol_version >= 27)
218                 mdfour_update(&md, (uchar *)md.buffer, sumresidue);
219
220         mdfour_result(&md, (uchar *)sum);
221
222         return MD4_DIGEST_LEN;
223 }