1 /* $Id: verify.c,v 1.3 2005/05/13 18:52:06 harbourn Exp $
2  * dcfldd - The Enhanced Forensic DD
3  * By Nicholas Harbour
4  */
5 
6 /* Copyright 85, 90, 91, 1995-2001, 2005 Free Software Foundation, Inc.
7    Copyright 2015                        Joao Eriberto Mota Filho <eriberto@eriberto.pro.br>
8    Copyright 2019                        Bernhard Übelacker <bernhardu@mailbox.org>
9 
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2, or (at your option)
13    any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software Foundation,
22    Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.  */
23 
24 /* GNU dd originally written by Paul Rubin, David MacKenzie, and Stuart Kemp. */
25 
26 #include "dcfldd.h"
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <string.h>
30 #include "config.h"
31 #include "hash.h"
32 #include "getpagesize.h"
33 #include "safe-read.h"
34 #include "sizeprobe.h"
35 #include "pattern.h"
36 #include "util.h"
37 #include "log.h"
38 
39 static int verify_update(hashlist_t *, void *, void *, size_t, size_t);
40 static void verify_remainder(hashlist_t *);
41 
42 /* The name of the verify file, or NULL if none.
43  * Verify file is used as a secondary input file and the input
44  * file is compared against it via the use of their hashes. */
45 char *verify_file = NULL;
46 int verify_fd;
47 FILE *verify_log;
48 
49 /* Skip this many records of `input_blocksize' bytes before reading */
50 uintmax_t vskip_records = 0;
51 
verify_update(hashlist_t * hashl,void * ibuf,void * vbuf,size_t ilen,size_t vlen)52 static int verify_update(hashlist_t *hashl,
53                          void *ibuf, void *vbuf,
54                          size_t ilen, size_t vlen)
55 {
56     size_t left_in_window = hash_windowlen - (bytes_in_window);
57     int cmp = 0;
58 
59     if (bytes_in_total == 0) {
60         hashl_init(hashl, TOTAL_CTX);
61         hashl_init(hashl, VTOTAL_CTX);
62     }
63 
64     if (hash_windowlen == 0) {
65         hash_update_buf(hashl, WINDOW_CTX, TOTAL_CTX, ibuf, ilen);
66         hash_update_buf(hashl, VWINDOW_CTX, VTOTAL_CTX, vbuf, vlen);
67     } else {
68         if (bytes_in_window == 0) {
69             hashl_init(hashl, WINDOW_CTX);
70             hashl_init(hashl, VWINDOW_CTX);
71         }
72 
73         if (ilen >= left_in_window || vlen >= left_in_window) {
74             char *ihash, *vhash;
75 
76             hash_update_buf(hashl, WINDOW_CTX, TOTAL_CTX,
77                             ibuf, min(ilen, left_in_window));
78             hash_update_buf(hashl, VWINDOW_CTX, VTOTAL_CTX,
79                             vbuf, min(vlen, left_in_window));
80 
81             /* if verify ever wants to do more than one hash, change this */
82             hashl_final(hashl, WINDOW_CTX);
83             ihash = strdup(hashl->hash->hashstr_buf);
84             hashl_final(hashl, VWINDOW_CTX);
85             vhash = hashl->hash->hashstr_buf;
86 
87             cmp = memcmp(ihash, vhash, hashl->hash->hashstr_buf_size);
88             free(ihash);
89 
90             if (cmp != 0)
91             {
92                 log_verifywindow(hashl->hash, window_beginning,
93                                  (window_beginning + hash_windowlen), cmp);
94                 return 1;
95             }
96 
97             window_beginning += hash_windowlen;
98 
99             bytes_in_window = 0;
100 
101             verify_update(hashl, ibuf + left_in_window, vbuf + left_in_window,
102                           ilen - left_in_window, vlen - left_in_window);
103         } else {
104             hash_update_buf(hashl, WINDOW_CTX, TOTAL_CTX, ibuf, ilen);
105             hash_update_buf(hashl, VWINDOW_CTX, VTOTAL_CTX, vbuf, vlen);
106         }
107     }
108 
109     return 0;
110 }
111 
verify_remainder(hashlist_t * hashl)112 static void verify_remainder(hashlist_t *hashl)
113 {
114     int cmp = 0;
115 
116     if (hash_windowlen > 0 && bytes_in_window > 0) {
117         char *ihash, *vhash;
118 
119         hashl_final(hashl, WINDOW_CTX);
120         ihash = strdup(hashl->hash->hashstr_buf);
121         hashl_final(hashl, VWINDOW_CTX);
122         vhash = hashl->hash->hashstr_buf;
123 
124         cmp = memcmp(ihash, vhash, hashl->hash->hashstr_buf_size);
125         free(ihash);
126 
127         if (cmp != 0)
128             log_verifywindow(hashl->hash, window_beginning,
129                              (window_beginning + hash_windowlen), cmp);
130     }
131 }
132 
133 /* The main loop when using the verify option. */
dd_verify(void)134 int dd_verify(void)
135 {
136     unsigned char *ibuf; /* Input buffer. */
137     unsigned char *vbuf; /* Verify buffer. */
138     unsigned char *real_ibuf;
139     unsigned char *real_vbuf;
140     ssize_t i_nread;		/* Bytes read in the current input block. */
141     ssize_t v_nread;        /* Bytes read in the current verify block. */
142     int exit_status = 0;
143     int input_from_stream = !!input_file;
144     int input_from_pattern = !input_from_stream;
145     size_t page_size = getpagesize();
146     size_t n_bytes_read;
147     char *i_hashstr_buf;
148     char *v_hashstr_buf;
149     size_t left_in_window;
150     int mismatch = 0;
151     int cmp = 0;
152 
153     real_ibuf = (unsigned char *) malloc(input_blocksize
154                                          + 2 * SWAB_ALIGN_OFFSET
155                                          + 2 * page_size - 1);
156     ibuf = real_ibuf;
157     ibuf += SWAB_ALIGN_OFFSET;	/* allow space for swab */
158 
159     ibuf = PTR_ALIGN(ibuf, page_size);
160 
161     real_vbuf = (unsigned char *) malloc(input_blocksize
162                                          + 2 * SWAB_ALIGN_OFFSET
163                                          + 2 * page_size - 1);
164     vbuf = real_vbuf;
165     vbuf += SWAB_ALIGN_OFFSET;	/* allow space for swab */
166 
167     vbuf = PTR_ALIGN(vbuf, page_size);
168 
169     i_hashstr_buf = malloc(hashstr_buf_size);
170     v_hashstr_buf = malloc(hashstr_buf_size);
171 
172     if (!input_from_pattern)
173         if (skip_records != 0)
174             skip(STDIN_FILENO, input_file, skip_records, input_blocksize, ibuf);
175 
176     if (vskip_records != 0)
177         skip(verify_fd, verify_file, vskip_records, input_blocksize, vbuf);
178 
179     if (max_records == 0)
180         quit(exit_status);
181 
182     if (input_from_pattern) {
183         replicate_pattern(pattern, ibuf, input_blocksize);
184         i_nread = input_blocksize;
185     }
186 
187     while (1)
188     {
189         /* Display an update message */
190         if (do_status && w_full % update_thresh == 0 && w_full != 0)
191         {
192             off_t total_bytes = w_full * input_blocksize;
193             off_t total_mb = total_bytes / 1048576;
194 
195             if (probe == PROBE_NONE || probed_size == 0)
196                 fprintf(stderr, "\r%llu blocks (%lluMb) written.",
197                         /* [FIX] verify.c:195:25: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument {3,4} has type ‘uintmax_t’ [-Wformat=] */
198                         (long long unsigned int) w_full, (long long unsigned int) total_mb);
199             else {
200                 time_t curr_time = time(NULL);
201                 int seconds = (int)difftime(curr_time, start_time);
202                 off_t probed_mb = probed_size / 1048576;
203                 float fprcnt = total_bytes / (float)probed_size;
204                 float fprcnt_remaining = 1.0 - fprcnt;
205                 int prcnt = (int)(fprcnt * 100);
206                 int seconds_remaining = (int)(seconds *
207                                               (fprcnt_remaining / fprcnt));
208                 char secstr[100];
209 
210                 time_left(secstr, sizeof secstr, seconds_remaining);
211                 fprintf(stderr,
212                         "\r[%d%% of %lluMb] %llu blocks (%lluMb) written. %s",
213                         /* [FIX] verify.c:210:25: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument {4,5,6} has type ‘off_t’ [-Wformat=] */
214                         prcnt, (long long unsigned int) probed_mb, (long long unsigned int) w_full, (long long unsigned int) total_mb, secstr);
215             }
216         }
217 
218         if (r_partial + r_full >= max_records)
219             break;
220 
221         v_nread = safe_read(verify_fd, vbuf, input_blocksize);
222 
223         if (v_nread < 0)
224             syscall_error(input_file);
225 
226         /* Zero the buffer before reading, so that if we get a read error,
227            whatever data we are able to read is followed by zeros.
228            This minimizes data loss. */
229         if (input_from_pattern) {
230             replicate_pattern(pattern, ibuf, v_nread);
231             i_nread = v_nread;
232         } else
233             i_nread = safe_read(STDIN_FILENO, ibuf, input_blocksize);
234 
235         if (i_nread < 0 && !input_from_pattern)
236             syscall_error(input_file);
237 
238         if (i_nread == 0 && v_nread == 0)
239             break;
240 
241         left_in_window = hash_windowlen - bytes_in_window;
242         mismatch = verify_update(ihashlist, ibuf, vbuf, i_nread, v_nread);
243 
244         if (i_nread != v_nread || (mismatch && i_nread < left_in_window)) {
245             log_verifywindow(ihashlist->hash, window_beginning,
246                              (window_beginning + bytes_in_window), 1);
247             mismatch = 1;
248         }
249 
250         if (mismatch)
251             break;
252     }
253 
254     free(real_ibuf);
255     free(real_vbuf);
256 
257     /* verifying a remainder and total wouldnt make sense if we
258      * they won't match due to different amounts read.
259      */
260     if (!mismatch) {
261         char *ihash, *vhash;
262 
263         verify_remainder(ihashlist);
264 
265         hashl_final(ihashlist, TOTAL_CTX);
266         ihash = strdup(ihashlist->hash->hashstr_buf);
267         hashl_final(ihashlist, VTOTAL_CTX);
268         vhash = ihashlist->hash->hashstr_buf;
269 
270         cmp = memcmp(ihash, vhash, ihashlist->hash->hashstr_buf_size);
271         free(ihash);
272 
273         if (cmp != 0)
274             log_verifywindow(ihashlist->hash, window_beginning,
275                              (window_beginning + bytes_in_window), cmp);
276 
277         log_verifytotal(ihashlist->hash, cmp);
278     } else
279         log_verifytotal(ihashlist->hash, 1);
280 
281     return exit_status;
282 }
283