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