Preserve the right errno value when trying adjunct functions during
[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-2008 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 int csum_length = SHORT_SUM_LENGTH; /* initial value */
28
29 /*
30   a simple 32 bit checksum that can be upadted from either end
31   (inspired by Mark Adler's Adler-32 checksum)
32   */
33 uint32 get_checksum1(char *buf1, int32 len)
34 {
35     int32 i;
36     uint32 s1, s2;
37     schar *buf = (schar *)buf1;
38
39     s1 = s2 = 0;
40     for (i = 0; i < (len-4); i+=4) {
41         s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] +
42           10*CHAR_OFFSET;
43         s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET);
44     }
45     for (; i < len; i++) {
46         s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
47     }
48     return (s1 & 0xffff) + (s2 << 16);
49 }
50
51
52 void get_checksum2(char *buf, int32 len, char *sum)
53 {
54         md_context m;
55
56         if (protocol_version >= 30) {
57                 uchar seedbuf[4];
58                 md5_begin(&m);
59                 md5_update(&m, (uchar *)buf, len);
60                 if (checksum_seed) {
61                         SIVAL(seedbuf, 0, checksum_seed);
62                         md5_update(&m, seedbuf, 4);
63                 }
64                 md5_result(&m, (uchar *)sum);
65         } else {
66                 int32 i;
67                 static char *buf1;
68                 static int32 len1;
69
70                 mdfour_begin(&m);
71
72                 if (len > len1) {
73                         if (buf1)
74                                 free(buf1);
75                         buf1 = new_array(char, len+4);
76                         len1 = len;
77                         if (!buf1)
78                                 out_of_memory("get_checksum2");
79                 }
80
81                 memcpy(buf1, buf, len);
82                 if (checksum_seed) {
83                         SIVAL(buf1,len,checksum_seed);
84                         len += 4;
85                 }
86
87                 for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
88                         mdfour_update(&m, (uchar *)(buf1+i), CSUM_CHUNK);
89
90                 /*
91                  * Prior to version 27 an incorrect MD4 checksum was computed
92                  * by failing to call mdfour_tail() for block sizes that
93                  * are multiples of 64.  This is fixed by calling mdfour_update()
94                  * even when there are no more bytes.
95                  */
96                 if (len - i > 0 || protocol_version >= 27)
97                         mdfour_update(&m, (uchar *)(buf1+i), len-i);
98
99                 mdfour_result(&m, (uchar *)sum);
100         }
101 }
102
103 void file_checksum(char *fname, char *sum, OFF_T size)
104 {
105         struct map_struct *buf;
106         OFF_T i, len = size;
107         md_context m;
108         int32 remainder;
109         int fd;
110
111         memset(sum, 0, MAX_DIGEST_LEN);
112
113         fd = do_open(fname, O_RDONLY, 0);
114         if (fd == -1)
115                 return;
116
117         buf = map_file(fd, size, MAX_MAP_SIZE, CSUM_CHUNK);
118
119         if (protocol_version >= 30) {
120                 md5_begin(&m);
121
122                 for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
123                         md5_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
124                                    CSUM_CHUNK);
125                 }
126
127                 remainder = (int32)(len - i);
128                 if (remainder > 0)
129                         md5_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
130
131                 md5_result(&m, (uchar *)sum);
132         } else {
133                 mdfour_begin(&m);
134
135                 for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
136                         mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
137                                       CSUM_CHUNK);
138                 }
139
140                 /* Prior to version 27 an incorrect MD4 checksum was computed
141                  * by failing to call mdfour_tail() for block sizes that
142                  * are multiples of 64.  This is fixed by calling mdfour_update()
143                  * even when there are no more bytes. */
144                 remainder = (int32)(len - i);
145                 if (remainder > 0 || protocol_version >= 27)
146                         mdfour_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
147
148                 mdfour_result(&m, (uchar *)sum);
149         }
150
151         close(fd);
152         unmap_file(buf);
153 }
154
155 static int32 sumresidue;
156 static md_context md;
157
158 void sum_init(int seed)
159 {
160         char s[4];
161
162         if (protocol_version >= 30)
163                 md5_begin(&md);
164         else {
165                 mdfour_begin(&md);
166                 sumresidue = 0;
167                 SIVAL(s, 0, seed);
168                 sum_update(s, 4);
169         }
170 }
171
172 /**
173  * Feed data into an MD4 accumulator, md.  The results may be
174  * retrieved using sum_end().  md is used for different purposes at
175  * different points during execution.
176  *
177  * @todo Perhaps get rid of md and just pass in the address each time.
178  * Very slightly clearer and slower.
179  **/
180 void sum_update(const char *p, int32 len)
181 {
182         if (protocol_version >= 30) {
183                 md5_update(&md, (uchar *)p, len);
184                 return;
185         }
186
187         if (len + sumresidue < CSUM_CHUNK) {
188                 memcpy(md.buffer + sumresidue, p, len);
189                 sumresidue += len;
190                 return;
191         }
192
193         if (sumresidue) {
194                 int32 i = CSUM_CHUNK - sumresidue;
195                 memcpy(md.buffer + sumresidue, p, i);
196                 mdfour_update(&md, (uchar *)md.buffer, CSUM_CHUNK);
197                 len -= i;
198                 p += i;
199         }
200
201         while (len >= CSUM_CHUNK) {
202                 mdfour_update(&md, (uchar *)p, CSUM_CHUNK);
203                 len -= CSUM_CHUNK;
204                 p += CSUM_CHUNK;
205         }
206
207         sumresidue = len;
208         if (sumresidue)
209                 memcpy(md.buffer, p, sumresidue);
210 }
211
212 int sum_end(char *sum)
213 {
214         if (protocol_version >= 30) {
215                 md5_result(&md, (uchar *)sum);
216                 return MD5_DIGEST_LEN;
217         }
218
219         if (sumresidue || protocol_version >= 27)
220                 mdfour_update(&md, (uchar *)md.buffer, sumresidue);
221
222         mdfour_result(&md, (uchar *)sum);
223
224         return MD4_DIGEST_LEN;
225 }