/* $Id: verify.c,v 1.3 2005/05/13 18:52:06 harbourn Exp $ * dcfldd - The Enhanced Forensic DD * By Nicholas Harbour */ /* Copyright (C) 85, 90, 91, 1995-2001, 2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* GNU dd originally written by Paul Rubin, David MacKenzie, and Stuart Kemp. */ #include #include #include #include "config.h" #include "dcfldd.h" #include "hash.h" #include "getpagesize.h" #include "safe-read.h" #include "sizeprobe.h" #include "pattern.h" #include "util.h" #include "log.h" static int verify_update(hashlist_t *, void *, void *, size_t, size_t); static void verify_remainder(hashlist_t *); /* The name of the verify file, or NULL if none. * Verify file is used as a secondary input file and the input * file is compared against it via the use of their hashes. */ char *verify_file = NULL; int verify_fd; FILE *verify_log; /* Skip this many records of `input_blocksize' bytes before reading */ uintmax_t vskip_records = 0; static int verify_update(hashlist_t *hashl, void *ibuf, void *vbuf, size_t ilen, size_t vlen) { size_t left_in_window = hash_windowlen - (bytes_in_window); int cmp = 0; if (bytes_in_total == 0) { hashl_init(hashl, TOTAL_CTX); hashl_init(hashl, VTOTAL_CTX); } if (hash_windowlen == 0) { hash_update_buf(hashl, WINDOW_CTX, TOTAL_CTX, ibuf, ilen); hash_update_buf(hashl, VWINDOW_CTX, VTOTAL_CTX, vbuf, vlen); } else { if (bytes_in_window == 0) { hashl_init(hashl, WINDOW_CTX); hashl_init(hashl, VWINDOW_CTX); } if (ilen >= left_in_window || vlen >= left_in_window) { char *ihash, *vhash; hash_update_buf(hashl, WINDOW_CTX, TOTAL_CTX, ibuf, min(ilen, left_in_window)); hash_update_buf(hashl, VWINDOW_CTX, VTOTAL_CTX, vbuf, min(vlen, left_in_window)); /* if verify ever wants to do more than one hash, change this */ hashl_final(hashl, WINDOW_CTX); ihash = strdup(hashl->hash->hashstr_buf); hashl_final(hashl, VWINDOW_CTX); vhash = hashl->hash->hashstr_buf; cmp = memcmp(ihash, vhash, hashl->hash->hashstr_buf_size); free(ihash); if (cmp != 0) { log_verifywindow(hashl->hash, window_beginning, (window_beginning + hash_windowlen), cmp); return 1; } window_beginning += hash_windowlen; bytes_in_window = 0; verify_update(hashl, ibuf + left_in_window, vbuf + left_in_window, ilen - left_in_window, vlen - left_in_window); } else { hash_update_buf(hashl, WINDOW_CTX, TOTAL_CTX, ibuf, ilen); hash_update_buf(hashl, VWINDOW_CTX, VTOTAL_CTX, vbuf, vlen); } } return 0; } static void verify_remainder(hashlist_t *hashl) { int cmp = 0; if (hash_windowlen > 0 && bytes_in_window > 0) { char *ihash, *vhash; hashl_final(hashl, WINDOW_CTX); ihash = strdup(hashl->hash->hashstr_buf); hashl_final(hashl, VWINDOW_CTX); vhash = hashl->hash->hashstr_buf; cmp = memcmp(ihash, vhash, hashl->hash->hashstr_buf_size); free(ihash); if (cmp != 0) log_verifywindow(hashl->hash, window_beginning, (window_beginning + hash_windowlen), cmp); } } /* The main loop when using the verify option. */ int dd_verify(void) { unsigned char *ibuf; /* Input buffer. */ unsigned char *vbuf; /* Verify buffer. */ unsigned char *real_ibuf; unsigned char *real_vbuf; ssize_t i_nread; /* Bytes read in the current input block. */ ssize_t v_nread; /* Bytes read in the current verify block. */ int exit_status = 0; int input_from_stream = !!input_file; int input_from_pattern = !input_from_stream; size_t page_size = getpagesize(); size_t n_bytes_read; char *i_hashstr_buf; char *v_hashstr_buf; size_t left_in_window; int mismatch = 0; int cmp = 0; real_ibuf = (unsigned char *) malloc(input_blocksize + 2 * SWAB_ALIGN_OFFSET + 2 * page_size - 1); ibuf = real_ibuf; ibuf += SWAB_ALIGN_OFFSET; /* allow space for swab */ ibuf = PTR_ALIGN(ibuf, page_size); real_vbuf = (unsigned char *) malloc(input_blocksize + 2 * SWAB_ALIGN_OFFSET + 2 * page_size - 1); vbuf = real_vbuf; vbuf += SWAB_ALIGN_OFFSET; /* allow space for swab */ vbuf = PTR_ALIGN(vbuf, page_size); i_hashstr_buf = malloc(hashstr_buf_size); v_hashstr_buf = malloc(hashstr_buf_size); if (!input_from_pattern) if (skip_records != 0) skip(STDIN_FILENO, input_file, skip_records, input_blocksize, ibuf); if (vskip_records != 0) skip(verify_fd, verify_file, vskip_records, input_blocksize, vbuf); if (max_records == 0) quit(exit_status); if (input_from_pattern) { replicate_pattern(pattern, ibuf, input_blocksize); i_nread = input_blocksize; } while (1) { /* Display an update message */ if (do_status && w_full % update_thresh == 0 && w_full != 0) { off_t total_bytes = w_full * input_blocksize; off_t total_mb = total_bytes / 1048576; if (probe == PROBE_NONE || probed_size == 0) fprintf(stderr, "\r%llu blocks (%lluMb) written.", w_full, total_mb); else { time_t curr_time = time(NULL); int seconds = (int)difftime(curr_time, start_time); off_t probed_mb = probed_size / 1048576; float fprcnt = total_bytes / (float)probed_size; float fprcnt_remaining = 1.0 - fprcnt; int prcnt = (int)(fprcnt * 100); int seconds_remaining = (int)(seconds * (fprcnt_remaining / fprcnt)); char secstr[100]; time_left(secstr, sizeof secstr, seconds_remaining); fprintf(stderr, "\r[%d%% of %lluMb] %llu blocks (%lluMb) written. %s", prcnt, probed_mb, w_full, total_mb, secstr); } } if (r_partial + r_full >= max_records) break; v_nread = safe_read(verify_fd, vbuf, input_blocksize); if (v_nread < 0) syscall_error(input_file); /* Zero the buffer before reading, so that if we get a read error, whatever data we are able to read is followed by zeros. This minimizes data loss. */ if (input_from_pattern) { replicate_pattern(pattern, ibuf, v_nread); i_nread = v_nread; } else i_nread = safe_read(STDIN_FILENO, ibuf, input_blocksize); if (i_nread < 0 && !input_from_pattern) syscall_error(input_file); if (i_nread == 0 && v_nread == 0) break; left_in_window = hash_windowlen - bytes_in_window; mismatch = verify_update(ihashlist, ibuf, vbuf, i_nread, v_nread); if (i_nread != v_nread || (mismatch && i_nread < left_in_window)) { log_verifywindow(ihashlist->hash, window_beginning, (window_beginning + bytes_in_window), 1); mismatch = 1; } if (mismatch) break; } free(real_ibuf); free(real_vbuf); /* verifying a remainder and total wouldnt make sense if we * they won't match due to different amounts read. */ if (!mismatch) { char *ihash, *vhash; verify_remainder(ihashlist); hashl_final(ihashlist, TOTAL_CTX); ihash = strdup(ihashlist->hash->hashstr_buf); hashl_final(ihashlist, VTOTAL_CTX); vhash = ihashlist->hash->hashstr_buf; cmp = memcmp(ihash, vhash, ihashlist->hash->hashstr_buf_size); free(ihash); if (cmp != 0) log_verifywindow(ihashlist->hash, window_beginning, (window_beginning + bytes_in_window), cmp); log_verifytotal(ihashlist->hash, cmp); } else log_verifytotal(ihashlist->hash, 1); return exit_status; }