1 /* sum -- checksum and count the blocks in a file
2 Copyright (C) 1986-2023 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /* Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. */
18
19 /* Written by Kayvan Aghaiepour and David MacKenzie. */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include "system.h"
26 #include "human.h"
27 #include "sum.h"
28
29 #include <byteswap.h>
30 #ifdef WORDS_BIGENDIAN
31 # define SWAP(n) (n)
32 #else
33 # define SWAP(n) bswap_16 (n)
34 #endif
35
36 /* Calculate the checksum and the size in bytes of stream STREAM.
37 Return -1 on error, 0 on success. */
38
39 int
bsd_sum_stream(FILE * stream,void * resstream,uintmax_t * length)40 bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
41 {
42 int ret = -1;
43 size_t sum, n;
44 int checksum = 0; /* The checksum mod 2^16. */
45 uintmax_t total_bytes = 0; /* The number of bytes. */
46 static const size_t buffer_length = 32768;
47 uint8_t *buffer = malloc (buffer_length);
48
49 if (! buffer)
50 return -1;
51
52 /* Process file */
53 while (true)
54 {
55 sum = 0;
56
57 /* Read block */
58 while (true)
59 {
60 n = fread (buffer + sum, 1, buffer_length - sum, stream);
61 sum += n;
62
63 if (buffer_length == sum)
64 break;
65
66 if (n == 0)
67 {
68 if (ferror (stream))
69 goto cleanup_buffer;
70 goto final_process;
71 }
72
73 if (feof (stream))
74 goto final_process;
75 }
76
77 for (size_t i = 0; i < sum; i++)
78 {
79 checksum = (checksum >> 1) + ((checksum & 1) << 15);
80 checksum += buffer[i];
81 checksum &= 0xffff; /* Keep it within bounds. */
82 }
83 if (total_bytes + sum < total_bytes)
84 {
85 errno = EOVERFLOW;
86 goto cleanup_buffer;
87 }
88 total_bytes += sum;
89 }
90
91 final_process:;
92
93 for (size_t i = 0; i < sum; i++)
94 {
95 checksum = (checksum >> 1) + ((checksum & 1) << 15);
96 checksum += buffer[i];
97 checksum &= 0xffff; /* Keep it within bounds. */
98 }
99 if (total_bytes + sum < total_bytes)
100 {
101 errno = EOVERFLOW;
102 goto cleanup_buffer;
103 }
104 total_bytes += sum;
105
106 memcpy (resstream, &checksum, sizeof checksum);
107 *length = total_bytes;
108 ret = 0;
109 cleanup_buffer:
110 free (buffer);
111 return ret;
112 }
113
114 /* Calculate the checksum and the size in bytes of stream STREAM.
115 Return -1 on error, 0 on success. */
116
117 int
sysv_sum_stream(FILE * stream,void * resstream,uintmax_t * length)118 sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
119 {
120 int ret = -1;
121 size_t sum, n;
122 uintmax_t total_bytes = 0;
123 static const size_t buffer_length = 32768;
124 uint8_t *buffer = malloc (buffer_length);
125
126 if (! buffer)
127 return -1;
128
129 /* The sum of all the input bytes, modulo (UINT_MAX + 1). */
130 unsigned int s = 0;
131
132 /* Process file */
133 while (true)
134 {
135 sum = 0;
136
137 /* Read block */
138 while (true)
139 {
140 n = fread (buffer + sum, 1, buffer_length - sum, stream);
141 sum += n;
142
143 if (buffer_length == sum)
144 break;
145
146 if (n == 0)
147 {
148 if (ferror (stream))
149 goto cleanup_buffer;
150 goto final_process;
151 }
152
153 if (feof (stream))
154 goto final_process;
155 }
156
157 for (size_t i = 0; i < sum; i++)
158 s += buffer[i];
159 if (total_bytes + sum < total_bytes)
160 {
161 errno = EOVERFLOW;
162 goto cleanup_buffer;
163 }
164 total_bytes += sum;
165 }
166
167 final_process:;
168
169 for (size_t i = 0; i < sum; i++)
170 s += buffer[i];
171 if (total_bytes + sum < total_bytes)
172 {
173 errno = EOVERFLOW;
174 goto cleanup_buffer;
175 }
176 total_bytes += sum;
177
178 int r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
179 int checksum = (r & 0xffff) + (r >> 16);
180
181 memcpy (resstream, &checksum, sizeof checksum);
182 *length = total_bytes;
183 ret = 0;
184 cleanup_buffer:
185 free (buffer);
186 return ret;
187 }
188
189 /* Print the checksum and size (in 1024 byte blocks) to stdout.
190 If ARGS is true, also print the FILE name. */
191
192 void
output_bsd(char const * file,int binary_file,void const * digest,bool raw,bool tagged,unsigned char delim,bool args,uintmax_t length)193 output_bsd (char const *file, int binary_file, void const *digest,
194 bool raw, bool tagged, unsigned char delim, bool args,
195 uintmax_t length)
196 {
197 if (raw)
198 {
199 /* Output in network byte order (big endian). */
200 uint16_t out_int = *(int *)digest;
201 out_int = SWAP (out_int);
202 fwrite (&out_int, 1, 16/8, stdout);
203 return;
204 }
205
206 char hbuf[LONGEST_HUMAN_READABLE + 1];
207 printf ("%05d %5s", *(int *)digest,
208 human_readable (length, hbuf, human_ceiling, 1, 1024));
209 if (args)
210 printf (" %s", file);
211 putchar (delim);
212 }
213
214 /* Print the checksum and size (in 512 byte blocks) to stdout.
215 If ARGS is true, also print the FILE name. */
216
217 void
output_sysv(char const * file,int binary_file,void const * digest,bool raw,bool tagged,unsigned char delim,bool args,uintmax_t length)218 output_sysv (char const *file, int binary_file, void const *digest,
219 bool raw, bool tagged, unsigned char delim, bool args,
220 uintmax_t length)
221 {
222 if (raw)
223 {
224 /* Output in network byte order (big endian). */
225 uint16_t out_int = *(int *)digest;
226 out_int = SWAP (out_int);
227 fwrite (&out_int, 1, 16/8, stdout);
228 return;
229 }
230
231 char hbuf[LONGEST_HUMAN_READABLE + 1];
232 printf ("%d %s", *(int *)digest,
233 human_readable (length, hbuf, human_ceiling, 1, 512));
234 if (args)
235 printf (" %s", file);
236 putchar (delim);
237 }
238