[Forensics-changes] [SCM] debian-forensics/dc3dd branch, debian, updated. debian/7.0.0-1-1-gc1979cd

Christophe Monniez christophe.monniez at fccu.be
Tue Aug 9 09:41:25 UTC 2011


The following commit has been merged in the debian branch:
commit c1979cd332779a170279c91313ea9cb7d6ec10ce
Author: Christophe Monniez <christophe.monniez at fccu.be>
Date:   Tue Aug 9 11:24:17 2011 +0200

    Merging upstream version 7.1.614.

diff --git a/.prev-version b/.prev-version
index aea5e0b..66ce77b 100644
--- a/.prev-version
+++ b/.prev-version
@@ -1 +1 @@
-6.12.5
+7.0.0
diff --git a/.version b/.version
index 66ce77b..2e55b50 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-7.0.0
+7.1.614
diff --git a/ChangeLog b/ChangeLog
index bbaad77..40834c1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,344 +1,352 @@
-2009-08-28 Richard Cordovano <rcordovano at users.sourceforge.net>
-
-	* Replaced the byte-by-byte verification capability with a verification capability that
-     hashes imaging outputs and compares the hashes to the input hash. The new verification capability
-     does not require a second read of the device.
-	* Enhanced the ability to generate multiple outputs by adding the capability to combine split and unsplit
-     outputs.
-	* Added the ability for the user to specify a sector size via the command line.
-	* Added display/logging of results of device size probes. Size probes are now always performed, providing
-     run statistics in most use cases (reading from standard input excepted). 
-	* Added display/logging of size statistics for each file in an input or output split.
-	* Simplified the command line options and removed all legacy dd features not needed for imaging. The simplified
-     command line is more rigorously validated to reduce the likelihood of performing a run contrary to user 
-     intent in order to avoid a second read of a device.
-	* Removed the progress=on command line option and the cumbersome INFO signaling protocol for 
-     obtaining a progress report. Instead, a progress bar is always displayed.
-	* Added new DEFAULT_IMAGING_MODE compile flag support (equivalent to command line options:
-     recovererrs=on, grouperrs=on, idirect=on).
-	* Reduced the use of global variables from 71 to 9, reduced function lengths, and
-     removed several instances of code duplication. 
-	* Introduced a program architecture that replaces a single loop with a jobs abstraction, allowing execution
-     of an arbitrary chain of jobs, each composed of one or more tasks that execute in parallel. The new
-     program architecture is designed to allow for the transparent addition of multi-step processing
-     scenarios such as the new verification capability.      
-	         
-
-2009-05-11 Richard Cordovano <rcordovano at users.sourceforge.net>
-
-     * Added ability to generate multiple outputs simultaneously by supporting multiple
-       of, vf, and vfjoin command line arguments.
-
-2009-04-01 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Put hashing and disk I/O into dedicated threads to increase
-	  throughput.
-
-	  As a side effect, removed cbs and conv=ascii/ebcdic/ibm/(un)block/lcase/swab
-	  command-line options since they don't apply to the goal of disk imaging.
-
-	  Also removed hashconv option and set behavior to hashconv=after.
-
-
-2009-03-24 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Added ability to detect HPA / DCO hidden areas on ATA drives (Linux only)
-
-
-2009-03-16 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fix hashwindow - result buffer was too small, causing incorrect
-	  output on OS X
-
-
-2009-03-10 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fix blockbench.pl to automatically work on Mac OS X
-
-
-2009-02-02 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Print hashes to console when log is enabled
-
-
-2008-12-15 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Print version number in startup message
-
-
-2008-12-09 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fix crash when verifying against empty file
-
-
-2008-10-27 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fixed a bug that was causing incorrect hashes to be displayed when
-	  using ifjoin or reading from standard input. Output data file was
-	  not affected.
-
-
-2008-10-15 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Added wipe= to automatically wipe a device with zeros
-
-	* Fix --help formatting so man page gets formatted properly
-
-
-2008-10-06 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fix minor warnings: use correct types and printf formats
-
-
-2008-09-30 Andrew Medico <amedico at users.sourceforge.net>
-
-	*  Add Cygwin support
-
-
-2008-09-29 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fix a crash when invalid ifjoin/vfjoin patterns given 
-
-	* Fix error reporting to account for skip= offset when
-	  conv=sync,noerror not used.
-
-	* Refactor redundant code into functions
-
-	* Remove obsolete code
-
-
-2008-09-25 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fix a crash when seek=X option was used without conv=notrunc
-
-
-2008-09-11 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fix progress display when skip=X option is used
-
-
-2008-09-05 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Removed unused coreutils modules to fix static linking error on Solaris 9
-
-
-2008-09-04 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Updated base package to Coreutils version 6.12.
-
-
-2008-08-19 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Check that split size is a multiple of block size and print an
-	  error message at startup, instead of mysteriously failing during
-	  the run.
-
-	* Added "blockbench" script to easily test many block sizes and find
-	  the fastest option for imaging.
-
-
-2008-07-24 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Added ifjoin= and vfjoin= to input or verify against
-	  split files
-
-
-2008-06-30 Andrew Medico <amedico at users.sourceforge.net>
-
-	* In closing log message, indicate if process was aborted, terminated
-	  normally, or terminated due to errors.
-
-
-2008-06-27 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Print hash values so far when process is interrupted
-
-
-2008-06-26 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Change count, skip, and seek options to take sector counts
-	  instead of bytes
-
-
-2008-06-24 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Report sector address when errors occur in non-grouped mode
-
-
-2008-06-23 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fix compile-flag printer to handle DEFAULT_SIZEPROBE
-
-
-2008-06-19 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Enable dynamic error recovery automatically when conv=sync,noerror
-	  is specified
-
-	* Probe device sector size instead of hard-coding dynamic error
-	  recovery read size
-
-	* Change default block size to 32K
-
-	* Report progress and error positions in sectors (probed from device
-	  size) instead of blocks
-
-	* Fix error counting in dynamic error recovery mode
-
-
-2008-06-17 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Log compile-time flags to the log file
-
-
-2008-06-16 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Add "dynamic" block size mode for better error recovery.
-	  Now when errors are detected, dc3dd rereads the failed block
-	  one sector at a time. This lets users run dc3dd in a faster
-	  large-block mode without losing entire blocks of data when a
-	  bad sector is encountered.
-
-	* When verifying an image, say "X bytes compared" in progress line
-	  instead of "X bytes copied".
-
-
-2008-06-12 Andrew Medico <amedico at users.sourceforge.net>
-
-	* When count is specified, calculate completion percentage
-	  out of count*ibs, instead of total device size.
-
-
-2008-06-11 Andrew Medico <amedico at users.sourceforge.net>
-
-	* When writing a pattern, sizeprobe destination device for
-	  progress reporting.
-
-
-2008-06-10 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Added --flags command line option to display the binary's
-	  configure flags
-
-
-2008-06-05 Andrew Medico <amedico at users.sourceforge.net>
-
-	* Fixed wording of error log when I/O errors are detected
-	  in errors=group mode
-
-	* Save command-line string to log file
-
-	* Log start and end times to log file
-
-	* Flush log file so it gets written to disk even if the process
-	  is interrupted
-
-
-2008-02-29 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Fixed default hashing support when a hash log is not
-	  explicitly defined. For example, if the user configures
-	  the program with CFLAGS="-DDEFAULT_HASH_MD5" but doesn't
-	  specify a hash log, the hashes are now sent to stderr.
-
-
-2008-02-12 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Fixed progresscount default. I had included an extra
-	  underscore yesterday.
-
-	* Added ability to change DEFAULT_BLOCKSIZE as promised
-	  in the documentation.
-	
-
-2008-02-11 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Added DEFAULT_ values for some command line flags. These
-	  values should be passed in the configure CFLAGS options. 
-	  The specific values that can be passed:
-	    - DEFAULT_HASH_MD5, DEFAULT_HASH_SHA1, DEFAULT_HASH_SHA256,
-	      DEFAULT_HASH_SHA512 enable hashing algorithms
-	    - DEFAULT_HASHCONV_BEFORE sets hashconv=before and
-	      DEFAULT_HASHCONV_AFTER sets hashconv=after
-	    - DEFAULT_PROGRESS sets progress=on
-	    - DEFAULT_PROGRESSCOUNT sets progresscount=x
-	      (e.g. -DDEFAULT_PROGRESSCOUNT=1000000)
-	    - DEFAULT_SIZEPROBE sets sizeprobe=on
-
-	  Example:
-	  ./configure CFLAGS="-O2 -DDEFAULT_HASH_MD5 -DDEFAULT_HASH_SHA1"
-
-	  It should be noted that any default hashing algorithms are
-	  ignored if the user specifies a value for hash= on the command
-	  line. Note that when a default hashing algorithm is specified
-	  the program CANNOT be run without hashing enabled.
-
-	  All other values can be overrideen normally on the command line.
-
-	* Moved \r out of translated text in progress meter display.
-	  This should help us avoid ugly errors regarding having \r in there.
-
-	
-2008-02-07 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Updated base package to Coreutils version 6.10.
-	
-
-2008-01-29 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Changed error handling such that on a partial read
-	  the entire block is wiped with zeros. See bug 1881387.
-	
-	* Grouped errors now display the block numbers where
-	  the error occured, not the offset. See bug 1881383.
-
-	* Improved help message for splitformat option.
-
-	
-2008-01-19 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Updated documentation to reflect log appends, legal
-	  values for hashconv
-	
-
-2008-01-12 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Updated contact address to dc3dd at dc3.mil.
-	
-
-2008-01-10 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Changed log files to append messages rather than overwrite.
-	
-
-2008-01-09 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Updated dc3_error_tail to create copies of the args
-	  passed in just in case they need to be written twice
-	  (stderr and log file).
-	
-	* Added check to display grouped errors at end of input
-	
-	
-2007-12-22 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Fixed sizeprobe code for OS X. It will still need work
-	  for BSD and other non-Linux, non-Mac platforms.
-
-	
-2007-12-21 Jesse Kornblum <jessekornblum at users.sourceforge.net>
-
-	* Created new source code tree based on slimmed down version
-	  of GNU Coreutils suite. Replaced existing ChangeLogs with
-	  this one. This file will be for changes to the dc3dd suite.
-
-	* Replaced NEWS file with news for dc3dd only
-
-	* Stripped out unused programs from src and man directories
-
-	* Cleaned top level Makefile.am and configure.ac to exclude
-	  deleted programs.
-
-	* Edited man/Makefile.am to only handle dc3dd.1 
-
-	* Removed old directory and changelogs from coreutils
+2011-03-02 Richard Cordovano <rcordovano at users.sourceforge.net>
+
+	* Added the ability to specify log= and hlog= more than once.
+	* Added phod= and fhod= options. For an output that is a device, using phod= ("partially hashed output device")
+        causes dc3dd to compute hashes of only the bytes dc3dd wrote to the output device. If fhod= 
+        ("fully hashed output device") is specified instead, dc3dd will compute hashes of both the bytes dc3dd wrote 
+        and of the entire device. 
+
+2009-08-28 Richard Cordovano <rcordovano at users.sourceforge.net>
+
+	* Replaced the byte-by-byte verification capability with a verification capability that
+     hashes imaging outputs and compares the hashes to the input hash. The new verification capability
+     does not require a second read of the device.
+	* Enhanced the ability to generate multiple outputs by adding the capability to combine split and unsplit
+     outputs.
+	* Added the ability for the user to specify a sector size via the command line.
+	* Added display/logging of results of device size probes. Size probes are now always performed, providing
+     run statistics in most use cases (reading from standard input excepted). 
+	* Added display/logging of size statistics for each file in an input or output split.
+	* Simplified the command line options and removed all legacy dd features not needed for imaging. The simplified
+     command line is more rigorously validated to reduce the likelihood of performing a run contrary to user 
+     intent in order to avoid a second read of a device.
+	* Removed the progress=on command line option and the cumbersome INFO signaling protocol for 
+     obtaining a progress report. Instead, a progress bar is always displayed.
+	* Added new DEFAULT_IMAGING_MODE compile flag support (equivalent to command line options:
+     recovererrs=on, grouperrs=on, idirect=on).
+	* Reduced the use of global variables from 71 to 9, reduced function lengths, and
+     removed several instances of code duplication. 
+	* Introduced a program architecture that replaces a single loop with a jobs abstraction, allowing execution
+     of an arbitrary chain of jobs, each composed of one or more tasks that execute in parallel. The new
+     program architecture is designed to allow for the transparent addition of multi-step processing
+     scenarios such as the new verification capability.      
+	         
+
+2009-05-11 Richard Cordovano <rcordovano at users.sourceforge.net>
+
+     * Added ability to generate multiple outputs simultaneously by supporting multiple
+       of, vf, and vfjoin command line arguments.
+
+2009-04-01 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Put hashing and disk I/O into dedicated threads to increase
+	  throughput.
+
+	  As a side effect, removed cbs and conv=ascii/ebcdic/ibm/(un)block/lcase/swab
+	  command-line options since they don't apply to the goal of disk imaging.
+
+	  Also removed hashconv option and set behavior to hashconv=after.
+
+
+2009-03-24 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Added ability to detect HPA / DCO hidden areas on ATA drives (Linux only)
+
+
+2009-03-16 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fix hashwindow - result buffer was too small, causing incorrect
+	  output on OS X
+
+
+2009-03-10 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fix blockbench.pl to automatically work on Mac OS X
+
+
+2009-02-02 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Print hashes to console when log is enabled
+
+
+2008-12-15 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Print version number in startup message
+
+
+2008-12-09 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fix crash when verifying against empty file
+
+
+2008-10-27 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fixed a bug that was causing incorrect hashes to be displayed when
+	  using ifjoin or reading from standard input. Output data file was
+	  not affected.
+
+
+2008-10-15 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Added wipe= to automatically wipe a device with zeros
+
+	* Fix --help formatting so man page gets formatted properly
+
+
+2008-10-06 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fix minor warnings: use correct types and printf formats
+
+
+2008-09-30 Andrew Medico <amedico at users.sourceforge.net>
+
+	*  Add Cygwin support
+
+
+2008-09-29 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fix a crash when invalid ifjoin/vfjoin patterns given 
+
+	* Fix error reporting to account for skip= offset when
+	  conv=sync,noerror not used.
+
+	* Refactor redundant code into functions
+
+	* Remove obsolete code
+
+
+2008-09-25 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fix a crash when seek=X option was used without conv=notrunc
+
+
+2008-09-11 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fix progress display when skip=X option is used
+
+
+2008-09-05 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Removed unused coreutils modules to fix static linking error on Solaris 9
+
+
+2008-09-04 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Updated base package to Coreutils version 6.12.
+
+
+2008-08-19 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Check that split size is a multiple of block size and print an
+	  error message at startup, instead of mysteriously failing during
+	  the run.
+
+	* Added "blockbench" script to easily test many block sizes and find
+	  the fastest option for imaging.
+
+
+2008-07-24 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Added ifjoin= and vfjoin= to input or verify against
+	  split files
+
+
+2008-06-30 Andrew Medico <amedico at users.sourceforge.net>
+
+	* In closing log message, indicate if process was aborted, terminated
+	  normally, or terminated due to errors.
+
+
+2008-06-27 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Print hash values so far when process is interrupted
+
+
+2008-06-26 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Change count, skip, and seek options to take sector counts
+	  instead of bytes
+
+
+2008-06-24 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Report sector address when errors occur in non-grouped mode
+
+
+2008-06-23 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fix compile-flag printer to handle DEFAULT_SIZEPROBE
+
+
+2008-06-19 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Enable dynamic error recovery automatically when conv=sync,noerror
+	  is specified
+
+	* Probe device sector size instead of hard-coding dynamic error
+	  recovery read size
+
+	* Change default block size to 32K
+
+	* Report progress and error positions in sectors (probed from device
+	  size) instead of blocks
+
+	* Fix error counting in dynamic error recovery mode
+
+
+2008-06-17 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Log compile-time flags to the log file
+
+
+2008-06-16 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Add "dynamic" block size mode for better error recovery.
+	  Now when errors are detected, dc3dd rereads the failed block
+	  one sector at a time. This lets users run dc3dd in a faster
+	  large-block mode without losing entire blocks of data when a
+	  bad sector is encountered.
+
+	* When verifying an image, say "X bytes compared" in progress line
+	  instead of "X bytes copied".
+
+
+2008-06-12 Andrew Medico <amedico at users.sourceforge.net>
+
+	* When count is specified, calculate completion percentage
+	  out of count*ibs, instead of total device size.
+
+
+2008-06-11 Andrew Medico <amedico at users.sourceforge.net>
+
+	* When writing a pattern, sizeprobe destination device for
+	  progress reporting.
+
+
+2008-06-10 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Added --flags command line option to display the binary's
+	  configure flags
+
+
+2008-06-05 Andrew Medico <amedico at users.sourceforge.net>
+
+	* Fixed wording of error log when I/O errors are detected
+	  in errors=group mode
+
+	* Save command-line string to log file
+
+	* Log start and end times to log file
+
+	* Flush log file so it gets written to disk even if the process
+	  is interrupted
+
+
+2008-02-29 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Fixed default hashing support when a hash log is not
+	  explicitly defined. For example, if the user configures
+	  the program with CFLAGS="-DDEFAULT_HASH_MD5" but doesn't
+	  specify a hash log, the hashes are now sent to stderr.
+
+
+2008-02-12 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Fixed progresscount default. I had included an extra
+	  underscore yesterday.
+
+	* Added ability to change DEFAULT_BLOCKSIZE as promised
+	  in the documentation.
+	
+
+2008-02-11 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Added DEFAULT_ values for some command line flags. These
+	  values should be passed in the configure CFLAGS options. 
+	  The specific values that can be passed:
+	    - DEFAULT_HASH_MD5, DEFAULT_HASH_SHA1, DEFAULT_HASH_SHA256,
+	      DEFAULT_HASH_SHA512 enable hashing algorithms
+	    - DEFAULT_HASHCONV_BEFORE sets hashconv=before and
+	      DEFAULT_HASHCONV_AFTER sets hashconv=after
+	    - DEFAULT_PROGRESS sets progress=on
+	    - DEFAULT_PROGRESSCOUNT sets progresscount=x
+	      (e.g. -DDEFAULT_PROGRESSCOUNT=1000000)
+	    - DEFAULT_SIZEPROBE sets sizeprobe=on
+
+	  Example:
+	  ./configure CFLAGS="-O2 -DDEFAULT_HASH_MD5 -DDEFAULT_HASH_SHA1"
+
+	  It should be noted that any default hashing algorithms are
+	  ignored if the user specifies a value for hash= on the command
+	  line. Note that when a default hashing algorithm is specified
+	  the program CANNOT be run without hashing enabled.
+
+	  All other values can be overrideen normally on the command line.
+
+	* Moved \r out of translated text in progress meter display.
+	  This should help us avoid ugly errors regarding having \r in there.
+
+	
+2008-02-07 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Updated base package to Coreutils version 6.10.
+	
+
+2008-01-29 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Changed error handling such that on a partial read
+	  the entire block is wiped with zeros. See bug 1881387.
+	
+	* Grouped errors now display the block numbers where
+	  the error occured, not the offset. See bug 1881383.
+
+	* Improved help message for splitformat option.
+
+	
+2008-01-19 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Updated documentation to reflect log appends, legal
+	  values for hashconv
+	
+
+2008-01-12 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Updated contact address to dc3dd at dc3.mil.
+	
+
+2008-01-10 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Changed log files to append messages rather than overwrite.
+	
+
+2008-01-09 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Updated dc3_error_tail to create copies of the args
+	  passed in just in case they need to be written twice
+	  (stderr and log file).
+	
+	* Added check to display grouped errors at end of input
+	
+	
+2007-12-22 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Fixed sizeprobe code for OS X. It will still need work
+	  for BSD and other non-Linux, non-Mac platforms.
+
+	
+2007-12-21 Jesse Kornblum <jessekornblum at users.sourceforge.net>
+
+	* Created new source code tree based on slimmed down version
+	  of GNU Coreutils suite. Replaced existing ChangeLogs with
+	  this one. This file will be for changes to the dc3dd suite.
+
+	* Replaced NEWS file with news for dc3dd only
+
+	* Stripped out unused programs from src and man directories
+
+	* Cleaned top level Makefile.am and configure.ac to exclude
+	  deleted programs.
+
+	* Edited man/Makefile.am to only handle dc3dd.1 
+
+	* Removed old directory and changelogs from coreutils
diff --git a/Options_Reference.txt b/Options_Reference.txt
index 958237a..79c1c0f 100644
--- a/Options_Reference.txt
+++ b/Options_Reference.txt
@@ -1,6 +1,6 @@
------
+------
 usage:
------
+------
 
 	./dc3dd [OPTION 1] [OPTION 2] ... [OPTION N]
 
@@ -16,36 +16,40 @@ usage:
 basic options:
 --------------
 
-	if=FILE              Read input from the device or regular file FILE
-	                     (see note #1 below). This option can only be used
-	                     once and cannot be combined with ifs=, pat=,
-	                     or tpat=.
+	if=DEVICE or FILE    Read input from a device or a file (see note #1
+	                     below for how to read from standard input). This
+	                     option can only be used once and cannot be
+	                     combined with ifs=, pat=, or tpat=.
 	ifs=BASE.FMT         Read input from a set of files with base name
 	                     BASE and sequential file name extensions
-	                     conforming to the format specifier FMT (see
-	                     note #4 below). This option can only be used once
-	                     and cannot be combined with if=, pat=, or
-	                     tpat=.
-	of=FILE              Write output to FILE (see note #2 below). This
+	                     conforming to the format specifier FMT (see note
+	                     #4 below for how to specify FMT). This option
+	                     can only be used once and cannot be combined with
+	                     if=, pat=, or tpat=.
+	of=FILE or DEVICE    Write output to a file or device (see note #2
+	                     below for how to write to standard output). This
+	                     option can be used more than once (see note #3
+	                     below for how to generate multiple outputs).
+	hof=FILE or DEVICE   Write output to a file or device, hash the
+	                     output file or device, and verify by comparing
+	                     the output hash(es) to the input hash(es). This
 	                     option can be used more than once (see note #3
-	                     below).
-	hof=FILE             Write output to FILE and verify FILE after
-                             writing it by hashing it and comparing the output
-                             hash(es) to the input hash(es). This option can be
-                             used more than once (see note #3 below).
+	                     below for how to generate multiple outputs).
 	ofs=BASE.FMT         Write output to a set of files with base name BASE
 	                     and sequential file name extensions generated from
-	                     the format specifier FMT (see note #4 below).  This
-	                     option can be used more than once (see note #3
-	                     below). Specify the maximum size of each file
-	                     in the set using ofsz=.
+	                     the format specifier FMT (see note #4 below for
+	                     how to specify FMT). This option can be used more
+	                     than once (see note #3 below for how to generate
+	                     multiple outputs). Specify the maximum size of
+	                     each file in the set using ofsz=.
 	hofs=BASE.FMT        Write output to a set of files with base name BASE
 	                     and sequential file name extensions generated from
-	                     the format specifier FMT (see note #4 below).
-	                     Verify the files after writing them by hashing
-	                     them and comparing the output hash(es) to the input
-	                     hash(es). This option can be used more than once
-	                     (see note #3 below). Specify the maximum size of
+	                     the format specifier FMT (see note #4 below for
+	                     how to specify FMT). Hash the output files and
+	                     verify by comparing the output hash(es) to the
+	                     input hash(es). This option can be used more than
+	                     once (see note #3 below for how to generate
+	                     multiple outputs). Specify the maximum size of
 	                     each file in the set using ofsz=.
 	ofsz=BYTES           Set the maximum size of each file in the sets of
 	                     files specified using ofs= or hofs= to
@@ -54,33 +58,45 @@ basic options:
 	                     -DDEFAULT_OUTPUT_FILE_SIZE followed by the desired
 	                     value in BYTES.
 	hash=ALGORITHM       Compute an ALGORITHM hash of the input and also
-	                     of any outputs specified using hof= or hofs=,
-	                     where ALGORITHM is one of md5, sha1, sha256, or
-	                     sha512. This option may be used once for each
-	                     supported ALGORITHM. Alternatively, hashing can
-	                     be activated at compile time using one or more of
-	                     -DDEFAULT_HASH_MD5,-DDEFAULT_HASH_SHA1,
+	                     of any outputs specified using hof=, hofs=, phod=,
+	                     or fhod=, where ALGORITHM is one of md5, sha1,
+	                     sha256, or sha512. This option may be used once
+	                     for each supported ALGORITHM. Alternatively,
+	                     hashing can be activated at compile time using one
+	                     or more of -DDEFAULT_HASH_MD5,-DDEFAULT_HASH_SHA1,
 	                     -DDEFAULT_HASH_SHA256, and -DDEFAULT_HASH_SHA512.
 	log=FILE             Log I/O statistcs, diagnostics, and total hashes
 	                     of input and output to FILE. If hlog= is not
 	                     specified, piecewise hashes of multiple file
-	                     input and output are also logged to FILE.
+	                     input and output are also logged to FILE. This
+	                     option can be used more than once to generate
+	                     multiple logs.
 	hlog=FILE            Log total hashes and piecewise hashes to FILE.
+	                     This option can be used more than once to generate
+	                     multiple logs.
 
 -----------------
 advanced options:
 -----------------
 
+	phod=DEVICE          The same as hof=DEVICE, except only the bytes
+	                     written to DEVICE by dc3dd are verified. This
+	                     option can be used more than once (see note
+	                     #3 below for how to generate multiple outputs).
+	fhod=DEVICE          The same as phod=DEVICE, with additional
+	                     hashing of the entire output DEVICE. This option
+	                     can be used more than once (see note #3 below
+	                     for how to generate multiple outputs).
 	rec=off              By default, zeros are written to the output(s) in
 	                     place of bad sectors when the input is a device.
 	                     Use this option to cause the program to instead
 	                     exit when a bad sector is encountered.
 	wipe=DEVICE          Wipe DEVICE by writing zeros (default) or a
 	                     pattern specified by pat= or tpat=.
-	vwipe=DEVICE         Wipe DEVICE by writing zeros (default) or a
-	                     pattern specified by pat= or tpat=.
-	                     Verify DEVICE after writing it by hashing it
-	                     and comparing the hash(es) to the input hash(es).
+	hwipe=DEVICE         Wipe DEVICE by writing zeros (default) or a
+	                     pattern specified by pat= or tpat=. Verify
+	                     DEVICE after writing it by hashing it and
+	                     comparing the hash(es) to the input hash(es).
 	pat=HEX              Use pattern as input, writing HEX to every byte
 	                     of the output. This option can only be used once
 	                     and cannot be combined with if=, ifs=, or
@@ -89,9 +105,9 @@ advanced options:
 	                     repeatedly to the output. This option can only be
 	                     used once and cannot be combined with if=, ifs=,
 	                     or pat=.
-	cnt=SECTORS          Input only SECTORS input sectors. Must be used with
-	                     pat= or tpat= if not using the pattern
-	                     with wipe= or vwipe= to wipe a device.
+	cnt=SECTORS          Read only SECTORS input sectors. Must be used
+	                     with pat= or tpat= if not using the pattern with
+	                     wipe= or hwipe= to wipe a device.
 	iskip=SECTORS        Skip SECTORS sectors at start of the input device
 	                     or file.
 	oskip=SECTORS        Skip SECTORS sectors at start of the output
@@ -112,9 +128,8 @@ advanced options:
 	verb=on              Activate verbose reporting, where sectors in/out
 	                     are reported for each file in sets of files
 	                     specified using ifs=, ofs=, or hofs=.
-	                     Alternatively, verbose reporting may be
-	                     activated at compile time using
-	                     -DDEFAULT_VERBOSE_REPORTING.
+	                     Alternatively, verbose reporting may be activated
+	                     at compile time using -DDEFAULT_VERBOSE_REPORTING.
 	nwspc=on             Activate compact reporting, where the use
 	                     of white space to divide log output into
 	                     logical sections is suppressed. Alternatively,
@@ -124,7 +139,7 @@ advanced options:
 	                     progress display reports 1000 bytes instead
 	                     of 1024 bytes as 1 KB. Alternatively, base 10
 	                     bytes reporting may be activated at compile
-	                     time using -DDEFAULT_DECIMAL_BYTES_REPORTING.
+	                     time using -DDEFAULT_BASE_TEN_BYTES_REPORTING.
 	corruptoutput=on     For verification testing and demonstration
 	                     purposes, corrupt the output file(s) with extra
 	                     bytes so a hash mismatch is guaranteed.
@@ -142,16 +157,16 @@ notes:
 ------
 
 1. To read from stdin, do not specify if=, ifs=, pat=, or tpat=.
-2. To write to stdout, do not specify of=, hof=, ofs=, hofs=, wipe=,
-   or vwipe=.
+2. To write to stdout, do not specify of=, hof=, ofs=, hofs=, phod=,
+   fhod=, wipe=, or hwipe=.
 3. To write to multiple outputs specify more than one of of=, hof=, ofs=,
-   or hofs=, in any combination.
+   hofs=, phod=, or fhod=, in any combination.
 4. FMT is a pattern for a sequence of file extensions that can be numerical
    starting at zero, numerical starting at one, or alphabetical. Specify FMT
    by using a series of zeros, ones, or a's, respectively. The number of
    characters used indicates the desired length of the extensions.
    For example, a FMT specifier of 1111 indicates four character
-   numerical extensions starting with 0001.
+   numerical extensions starting with 0000.
 5. BYTES may be followed by the following multiplicative suffixes:
    c (1), w (2), b (512), kB (1000), K (1024), MB (1000*1000),
    M (1024*1024), GB (1000*1000*1000), G (1024*1024*1024), and
diff --git a/README b/README
index 77b4c7f..c6945f4 100644
--- a/README
+++ b/README
@@ -1,38 +1,22 @@
-dc3dd 7.0.0
+dc3dd 7.1
 DCCI 
-Richard Cordovano
-2009-09-11
+2010-10-18
+
+Issues:
+-------
+When imaging to a device with verification under Mac OS X, verification
+can fail due to automatic mounting of the target device. One solution is
+to use Disk Arbitrator in conjunction with dc3dd on OS X.    
 
 New Features:
 -------------
-The command line options supported by dc3dd are significantly changed in
-7.0.0. For a full listing of the command line options, please consult
-Options_Reference.txt or execute dc3dd using the --help option:
-
-$ ./dc3dd --help 
-
-The command line is now more rigorously validated to reduce the
-likelihood of performing a run contrary to user intent. This is done
-to minimize reading of the input device. Examples of command lines for
-common uses of dc3dd are provided in Sample_Commands.txt.  
-
-Release 7.0.0 replaces the byte-by-byte comparison verification
-capability of prior releases with a verification capability that hashes
-imaging outputs and compares output hash(es) to input hash(es). This
-new verification capability does not require a second read of the input
-device. 
-
-It is now possible to specify more than one output file and to mix split
-and unsplit outputs, where splitting an output refers to writing to a
-set of files rather than to a single file.
-
-Devices are now always probed for size, and the size of the device in 
-sectors and sector size is reported. It is however, also now possible to
-explicitly specify the sector size dc3dd is to use, overriding the
-probed sector size (for devices) or assumed sector size (for files).  
+Log output may be sent to multiple job logs and hash logs. Simply specify
+log=LOG and/or hlog=LOG more than once.
 
-Progress display is now always on and various options for formatting
-output are now provided.
+Verification of an image restored to a device larger than the image is now
+supported. Specify phod=DEVICE to hash only the bytes dc3dd writes to the
+device. Specify fhod=DEVICE to hash both the bytes dc3dd writes to the
+device and all the bytes that follow, up to the end of the device.   
 
 Building dc3dd:
 ---------------
@@ -40,8 +24,8 @@ dc3dd is distributed as source code and must be compiled before use.
 The default configuration can be built and installed to /usr/local/bin
 with the following commands:
 
-$ tar zxvf dc3dd-7.0.0.tar.gz
-$ cd dc3dd-7.0.0
+$ tar zxvf dc3dd-7.1.tar.gz
+$ cd dc3dd-7.1
 $ ./configure
 $ make
 $ sudo make install
diff --git a/Sample_Commands.txt b/Sample_Commands.txt
index c76ea0e..1f7e1a2 100644
--- a/Sample_Commands.txt
+++ b/Sample_Commands.txt
@@ -12,23 +12,28 @@ $ ./dc3dd if=/dev/sda ofs=suspect.img.000 ofsz=650M hash=md5 hash=sha1
 log=suspect.txt
 
 Imaging a device to both a single output file and to a set of CD-sized
-output files with generation of md5 and sha1 hashes of the hard device:
+output files with generation of md5 and sha1 hashes of the device:
 
 $ ./dc3dd if=/dev/sda of=suspect.img of=suspect.img ofs=suspect.img.000
 ofsz=650M hash=md5 hash=sha1 log=suspect.txt
 
 Imaging a device to both a single output file and to a set of CD-sized
-output files with generation of md5 and sha1 hashes of the hard device
+output files with generation of md5 and sha1 hashes of the device
 and md5 and sha1 hashes of the outputs:
 
 $ ./dc3dd if=/dev/sda of=suspect.img hof=suspect.img hofs=suspect.img.000
 ofsz=650M hash=md5 hash=sha1 log=suspect.txt
 
-Restoring a set of image files to a blank drive with verification hashes of the drive after it is written:
-$ ./dc3dd ifs=suspect.img.000 hof=/dev/sdb hash=md5 hash=sha1 log=suspect-restore.txt
+Restoring a set of image files to a device with verification hashes of 
+only the bytes dc3dd writes to the device:
+$ ./dc3dd ifs=suspect.img.000 phod=/dev/sdb hash=md5 hash=sha1 log=suspect-restore.txt
+
+Restoring a set of image files to a device with verification hashes of 
+both the bytes dc3dd writes to the device and the entire device:
+$ ./dc3dd ifs=suspect.img.000 fhod=/dev/sdb hash=md5 hash=sha1 log=suspect-restore.txt
 
 Wiping a drive:
 $ ./dc3dd wipe=/dev/sdb log=wipe.txt
 
 Wiping a drive with verification:
-$ ./dc3dd vwipe=/dev/sdb hash=md5 hash=sha1 log=wipe.txt
+$ ./dc3dd hwipe=/dev/sdb hash=md5 hash=sha1 log=wipe.txt
diff --git a/configure b/configure
index 333de10..b47dafe 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.61 for dc3dd 7.0.0.
+# Generated by GNU Autoconf 2.61 for dc3dd 7.1.614.
 #
 # Report bugs to <dc3dd at dc3.mil>.
 #
@@ -574,8 +574,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
 # Identity of this package.
 PACKAGE_NAME='dc3dd'
 PACKAGE_TARNAME='dc3dd'
-PACKAGE_VERSION='7.0.0'
-PACKAGE_STRING='dc3dd 7.0.0'
+PACKAGE_VERSION='7.1.614'
+PACKAGE_STRING='dc3dd 7.1.614'
 PACKAGE_BUGREPORT='dc3dd at dc3.mil'
 
 ac_unique_file="src/dc3dd.c"
@@ -1586,7 +1586,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures dc3dd 7.0.0 to adapt to many kinds of systems.
+\`configure' configures dc3dd 7.1.614 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1656,7 +1656,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of dc3dd 7.0.0:";;
+     short | recursive ) echo "Configuration of dc3dd 7.1.614:";;
    esac
   cat <<\_ACEOF
 
@@ -1770,7 +1770,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-dc3dd configure 7.0.0
+dc3dd configure 7.1.614
 generated by GNU Autoconf 2.61
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1784,7 +1784,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by dc3dd $as_me 7.0.0, which was
+It was created by dc3dd $as_me 7.1.614, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   $ $0 $@
@@ -2594,7 +2594,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='dc3dd'
- VERSION='7.0.0'
+ VERSION='7.1.614'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -64904,7 +64904,7 @@ exec 6>&1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by dc3dd $as_me 7.0.0, which was
+This file was extended by dc3dd $as_me 7.1.614, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -64957,7 +64957,7 @@ Report bugs to <bug-autoconf at gnu.org>."
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF
 ac_cs_version="\\
-dc3dd config.status 7.0.0
+dc3dd config.status 7.1.614
 configured by $0, generated by GNU Autoconf 2.61,
   with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
diff --git a/lib/iconv_open-osf.h b/lib/iconv_open-osf.h
index 0187f23..4d35764 100644
--- a/lib/iconv_open-osf.h
+++ b/lib/iconv_open-osf.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.1 */
+/* ANSI-C code produced by gperf version 3.0.4 */
 /* Command-line: gperf -m 10 ./iconv_open-osf.gperf  */
 /* Computed positions: -k'4,$' */
 
@@ -251,6 +251,9 @@ static const struct mapping mappings[] =
 
 #ifdef __GNUC__
 __inline
+#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
+__attribute__ ((__gnu_inline__))
+#endif
 #endif
 const struct mapping *
 mapping_lookup (register const char *str, register unsigned int len)
diff --git a/man/dc3dd.1 b/man/dc3dd.1
index 6b07a43..8328ea2 100644
--- a/man/dc3dd.1
+++ b/man/dc3dd.1
@@ -1,5 +1,5 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.35.
-.TH DC3DD "1" "February 2010" "dc3dd 7.0.0" "User Commands"
+.TH DC3DD "1" "November 2010" "dc3dd 7.1.CREV" "User Commands"
 .SH NAME
 dc3dd \- convert and copy a file
 .SH DESCRIPTION
diff --git a/src/dc3dd.c b/src/dc3dd.c
index 5f78239..151ee08 100644
--- a/src/dc3dd.c
+++ b/src/dc3dd.c
@@ -1,5 +1,5 @@
 // dc3dd -- a dd for digital forensics.
-//   Copyright (C) 85, 90, 91, 1995-2009 Free Software Foundation, Inc.
+//   Copyright (C) 85, 90, 91, 1995-2008 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
@@ -40,10 +40,10 @@
 
 #ifdef __linux__
    #include <sys/mount.h>
-   # include <sys/mtio.h>
+   #include <sys/mtio.h>
    #ifdef USE_HDPARM
-      #include <linux/types.h>
-      #include "hdparm/hpa_dco.h"
+   #include <linux/types.h>
+   #include "hdparm/hpa_dco.h"
    #endif
 #elif defined (__APPLE__)
    #include <sys/disk.h>
@@ -68,10 +68,10 @@
   proper_name("Andrew Medico"), \
   proper_name("Richard Cordovano")
 
-// Keep it smaller than SIZE_MAX - alignment add-on bytes, to allow
-// allocating buffers that size. Keep it smaller than SSIZE_MAX, for the
-// benefit of system calls like read(). And keep it smaller than OFF_T_MAX,
-// for the benefit of lseek().
+// Keep block size smaller than SIZE_MAX - alignment bytes, to allow
+// allocating buffers that size. Keep block size smaller than SSIZE_MAX, for 
+// the benefit of system calls like read(). And keep block size smaller than
+// OFF_T_MAX, for the benefit of lseek().
 #define MAX_BLOCKSIZE() MIN (SIZE_MAX - (2 * getpagesize() - 1), MIN (SSIZE_MAX, OFF_T_MAX))
 
 #define STRINGIFY(s) #s
@@ -98,57 +98,70 @@ static const long int JOB_PROGRESS_INTERVAL_MILLISECS = 100;
 // ENUMS
 //-------------------------
 
-enum IO_STATE
-{
+enum LOGS {
+   ALL_LOGS,
+   JOB_LOGS,
+   HASH_LOGS
+};
+
+enum IO_STATE {
    PENDING,
    OPEN,
    COMPLETE,
    FATAL_ERROR
 };
 
-enum EXIT_CODE
-{
+enum EXIT_CODE {
    DC3DD_EXIT_CODE_NOT_SET = -1,
    DC3DD_EXIT_COMPLETED,
    DC3DD_EXIT_ABORTED,
    DC3DD_EXIT_FAILED,
 };
 
+enum VERIFICATION_TYPE {
+   NONE,
+   STANDARD,
+   DEVICE_PARTIAL,
+   DEVICE_FULL
+};
+
 //-------------------------
 // TYPEDEFS
 //-------------------------
 
-typedef struct _file_t 
-{
-   char* unparsed_name;
-   char* name;
+typedef struct _log_t {
+   FILE *file;
+   struct _log_t *next_log;
+} log_t;
+
+typedef struct _file_t {
+   char *unparsed_name;
+   char *name;
+   bool part_of_set;
    uintmax_t number;
    int flags;
    int descriptor;
-   bool probed;
    intmax_t offset;
-   bool is_device;
-   bool is_block_device;
+   bool probed;
    uintmax_t probed_size_in_bytes;
    uintmax_t probed_size_in_sectors;
    size_t probed_sector_size;
-   uintmax_t size_in_bytes;
-   bool part_of_set;
-   bool verify_requested;
-   struct _file_t* next_file;
-} 
-file_t;
-
-typedef struct _settings_t
-{
-   char* input_pattern_string;
-   char* input_pattern;
+   bool is_device;
+   bool is_block_device;
+   uintmax_t bytes_processed;
+   enum VERIFICATION_TYPE verification;
+   struct _file_t *next_file;
+} file_t;
+
+typedef struct _settings_t {
+   char *input_pattern_string;
+   char *input_pattern;
    size_t input_pattern_length;
-   file_t* input_file;
-   file_t* output_files;
-   file_t* wipe_target;
+   file_t *input_file;
+   file_t *output_files;
+   file_t *wipe_target;
    size_t sector_size;
-   const char* sector_size_source;
+   const char * sector_size_source;
    size_t buffer_size;
    uintmax_t input_sectors_to_skip;
    uintmax_t output_sectors_to_skip;
@@ -159,61 +172,47 @@ typedef struct _settings_t
    bool verifying_output;
    bool append_output;
    bool corrupt_output;
-} 
-settings_t;
+} settings_t;
 
 typedef void hash_init_func_t(void* ctx);
 typedef void hash_update_func_t(const void* buf, size_t len, void* ctx);
 typedef void hash_finish_func_t(void* ctx, void* buf);
 
-typedef struct _hash_algorithm_t
-{
+typedef struct _hash_algorithm_t {
    bool active;
-   char* name;
+   char *name;
    size_t context_size;
    size_t sum_size;
-   hash_init_func_t* init;
-   hash_update_func_t* update;
-   hash_finish_func_t* finish;
-}
-hash_algorithm_t;
-
-typedef struct _hash_t
-{
-   hash_algorithm_t* algorithm;
-   void* context;
-   char* sum;
-   char* result;
+   hash_init_func_t *init;
+   hash_update_func_t *update;
+   hash_finish_func_t *finish;
+} hash_algorithm_t;
+
+typedef struct _hash_t {
+   void *context;
+   char *sum;
+   char *result;
    uintmax_t bytes_hashed;
-   struct _hash_t* current_piece;
-   struct _hash_t* pieces;
-   uintmax_t piecewise_hash_length;
-   struct _hash_t* next_hash;
-}
-hash_t;
+   struct _hash_t *next_hash;
+} hash_t;
 
-typedef struct _buffer_t
-{
-   char* data;
+typedef struct _buffer_t {
+   char *data;
    size_t length;
-}
-buffer_t;
+} buffer_t;
 
-typedef struct _buffer_queue_t
-{
-   buffer_t* buffers;
+typedef struct _buffer_queue_t {
+   buffer_t *buffers;
    uint number_of_buffers;
    uint buffers_used;
    uint next_available_buffer;
-   pthread_mutex_t* lock;
-   pthread_cond_t* not_empty;
-   pthread_cond_t* not_full;
+   pthread_mutex_t *lock;
+   pthread_cond_t *not_empty;
+   pthread_cond_t *not_full;
    bool done_buffering;
-}
-buffer_queue_t;
+} buffer_queue_t;
 
-typedef struct _input_t
-{
+typedef struct _input_t {
    enum IO_STATE state;
    size_t buffer_size;
    buffer_t buffer;
@@ -236,67 +235,78 @@ typedef struct _input_t
    void (*open)(struct _input_t* input);
    void (*produce_bytes)(struct _input_t* input);
    void (*close)(struct _input_t* input);
-}
-input_t;
+} input_t;
+
+typedef struct _hash_output_t {
+   hash_algorithm_t *algorithm;
+   hash_t *total_hash;
+   uintmax_t total_hash_length;
+   hash_t *current_piece;
+   hash_t *piecewise_hash;
+   uintmax_t piecewise_hash_length;
+   hash_t *device_hash;
+   struct _hash_output_t *next;
+} hash_output_t;
 
-typedef struct _output_t
-{
+typedef struct _file_output_t {
+   file_t* current_file;
+   file_t* files;
+   uintmax_t max_file_size;
+   uintmax_t sectors_to_skip;
+   uintmax_t bytes_output;
+   enum VERIFICATION_TYPE verification;
+   hash_output_t* expected_hashes;
+   hash_output_t* actual_hashes;
+   bool append_garbage_bytes; // For testing purposes.
+} file_output_t;
+
+typedef struct _output_t {
    enum IO_STATE state; 
    pthread_t thread;
    size_t sector_size;
    uintmax_t buffer_size;
    buffer_queue_t* buffer_queue;
-   uintmax_t bytes_output;
-   hash_t* hash;
-   file_t* current_file;
-   file_t* files;
-   uintmax_t max_file_size;
-   uintmax_t sectors_to_skip;
-   bool append_garbage_bytes;
+   hash_output_t *hash;
+   file_output_t *file;
    void (*open)(struct _output_t* output);
    void (*consume_bytes)(struct _output_t* output, buffer_t* buffer);
    void (*close)(struct _output_t* output);
-   struct _output_t* expected_hashes;
-   struct _output_t* actual_hashes;
    struct _output_t* next_output;   
-}
-output_t;
+} output_t;
 
-typedef struct _task_t
-{
+typedef struct _task_t {
    pthread_t thread;
-   pthread_mutex_t* signalling_lock;
-   input_t* input;
-   output_t* outputs;
+   pthread_mutex_t *signaling_lock;
+   input_t *input;
+   output_t *outputs;
+   file_output_t *verification_target;
    bool completed;
    bool aborted;
    enum EXIT_CODE exit_code;
-   struct _task_t* next_task;   
-}
-task_t;
+   struct _task_t *next_task;   
+} task_t;
 
-typedef struct _job_t
-{
+typedef struct _job_t {
    pthread_t monitor_thread;
    task_t* tasks;
    long int progress_interval_in_ms;
    void (*report_progress)(struct _job_t* job, bool final);
    enum EXIT_CODE exit_code;
    struct _job_t* next_job;
-}
-job_t;
+} job_t;
 
 //-------------------------
 // GLOBAL VARIABLES
 //-------------------------
 
-char* program_name = NULL;
+char *program_name = NULL;
 static xtime_t start_time;
-static FILE* log = NULL;
-static FILE* hash_log = NULL;
+static log_t *job_logs = NULL;
+static log_t *hash_logs = NULL;
+static log_t *all_logs = NULL;
 static bool progress_displayed = false;
-static pthread_mutex_t signalling_lock;
-static pthread_cond_t* interrupted = 0;
+static pthread_mutex_t signaling_lock;
+static pthread_cond_t *interrupted = 0;
 
 #ifdef DEFAULT_VERBOSE_REPORTING
 static bool verbose_reporting = true;
@@ -316,12 +326,13 @@ static int progress_bytes_reporting_flag = 0;
 static int progress_bytes_reporting_flag = human_base_1024;
 #endif
 
-// A single lock is used for both the logs and the console so that the order of 
-// output will be the same everywhere.  
+// A single lock is used for both the job_logs and the console so that the 
+// ordering of the log messages will be the same on the console and in the
+// logs.  
 static pthread_mutex_t reporting_lock;
 
-static hash_algorithm_t hash_algorithms[] = 
-{
+// Currently supporting MD5, SHA1, SHA256, and SHA512.
+static hash_algorithm_t hash_algorithms[] = {
    { 
       #ifdef DEFAULT_HASH_MD5
       true,
@@ -334,9 +345,8 @@ static hash_algorithm_t hash_algorithms[] =
       (hash_init_func_t*)md5_init_ctx,
       (hash_update_func_t*)md5_process_bytes,
       (hash_finish_func_t*)md5_finish_ctx,
-    },
-    
-    {
+   },
+   {
       #ifdef DEFAULT_HASH_SHA1
       true,
       #else
@@ -348,9 +358,8 @@ static hash_algorithm_t hash_algorithms[] =
       (hash_init_func_t*)sha1_init_ctx,
       (hash_update_func_t*)sha1_process_bytes,
       (hash_finish_func_t*)sha1_finish_ctx,
-    },
-    
-    {
+   },
+   {
       #ifdef DEFAULT_HASH_SHA256
       true,
       #else
@@ -362,9 +371,8 @@ static hash_algorithm_t hash_algorithms[] =
       (hash_init_func_t*)sha256_init_ctx,
       (hash_update_func_t*)sha256_process_bytes,
       (hash_finish_func_t*)sha256_finish_ctx,
-    },
-    
-    {
+   },
+   {
       #ifdef DEFAULT_HASH_SHA512
       true,
       #else
@@ -376,8 +384,8 @@ static hash_algorithm_t hash_algorithms[] =
       (hash_init_func_t*)sha512_init_ctx,
       (hash_update_func_t*)sha512_process_bytes,
       (hash_finish_func_t*)sha512_finish_ctx,
-    }
-  };
+   }
+};
 
 //-------------------------
 // FUNCTIONS
@@ -386,35 +394,52 @@ static hash_algorithm_t hash_algorithms[] =
 void usage(int status);
 
 static void
-flush_logs()
-{
-   fflush(stderr);
-   if (log != NULL)
-   {
-      fflush(log);
-   }   
-   if (hash_log != NULL)
-   {
-      fflush(hash_log);
+terminate_logging() {
+   pthread_mutex_lock(&reporting_lock);
+
+   for (log_t *log = all_logs; log; log = log->next_log) {
+      fflush(log->file);
+      fclose(log->file);
    }   
+
+   pthread_mutex_unlock(&reporting_lock);
+   pthread_mutex_destroy(&reporting_lock);
 }
 
 static void
-terminate_logging()
-{
-   // Close the logs and tear down the reporting mutex. 
-   pthread_mutex_lock(&reporting_lock);
-   flush_logs();
-   if (log)
-   {
-      fclose(log);
-   }
-   if (hash_log)
-   {
-      fclose(hash_log);
+flush_logs() {
+   fflush(stderr);
+   for (log_t *log = all_logs; log; log = log->next_log)
+      fflush(log->file);
+}
+
+static void
+write_to_logs(const char *message, enum LOGS target) {
+   log_t *logs = NULL;
+   switch (target) {
+      case ALL_LOGS:
+         logs = all_logs;
+         break;
+      case JOB_LOGS:
+         logs = job_logs;
+         break;
+      case HASH_LOGS:
+         logs = hash_logs ? hash_logs : job_logs;
+         break;
    }
+
+   for (log_t *log = logs; log; log = log->next_log)
+      fputs(message, log->file);
+}
+
+static void
+report(const char *message, enum LOGS target) {
+   pthread_mutex_lock(&reporting_lock);
+
+   fputs(message, stderr);   
+   write_to_logs(message, target);
+
    pthread_mutex_unlock(&reporting_lock);
-   pthread_mutex_destroy(&reporting_lock);
 }
 
 // Begin code copied (and modified) from ../lib/error.c 
@@ -422,56 +447,52 @@ terminate_logging()
 #define __strerror_r strerror_r
 
 static void
-report_errno_message(int errnum)
-{
-   char const *s;
+append_system_error_message(int errnum) {
+   char const *s = NULL;
 
+  // Attempt to get the current error string.
 #if defined HAVE_STRERROR_R || _LIBC
    char errbuf[DISPLAY_MESSAGE_LENGTH];
-#if STRERROR_R_CHAR_P || _LIBC
+  #if STRERROR_R_CHAR_P || _LIBC
    s = __strerror_r(errnum, errbuf, sizeof errbuf);
-# else
+  #else
    if (__strerror_r(errnum, errbuf, sizeof errbuf) == 0)
-   {
       s = errbuf;
-   }
    else
-   {
-      s = 0;
-   }
-# endif
+      s = NULL;
+  #endif
 #else
    s = strerror(errnum);
 #endif
 
+  // Use a generic error string if the attempt to get the system error
+  // system string failed.
 #if !_LIBC
    if (!s)
-   {
       s = _("system error");
-   }
 #endif
 
 #if _LIBC
    __fxprintf(NULL, ": %s", s);
 #else
    fprintf(stderr, ": %s", s);
-   if (log != NULL)
-   {
-      fprintf(log, ": %s", s);
-   }
 #endif
+   for (log_t* log = job_logs; log; log = log->next_log)
+      fprintf(log->file, ": %s", s);
 }
 
 static void
-report_error_tail(int status, int errnum, const char *message, va_list args)
+write_error_message(int errnum, const char *message, va_list args)
 {
    va_list arg2;
    va_copy(arg2, args);
 
+   // Write the error message.
 #if _LIBC
    if (_IO_fwide (stderr, 0) > 0)
    {
-# define ALLOCA_LIMIT 2000
+      // Write the message using wide chars.
+      #define ALLOCA_LIMIT 2000
       size_t len = strlen(message) + 1;
       wchar_t *wmessage = NULL;
       mbstate_t st;
@@ -482,15 +503,11 @@ report_error_tail(int status, int errnum, const char *message, va_list args)
       while (1)
       {
          if (__libc_use_alloca(len * sizeof (wchar_t)))
-         {
             wmessage = (wchar_t *)alloca(len * sizeof (wchar_t));
-         }
          else
          {
             if (!use_malloc)
-            {
                wmessage = NULL;
-            }
 
             wchar_t *p = (wchar_t *)realloc(wmessage, len * sizeof (wchar_t));
             if (p == NULL)
@@ -502,14 +519,12 @@ report_error_tail(int status, int errnum, const char *message, va_list args)
             wmessage = p;
             use_malloc = true;
          }
+
          memset(&st, '\0', sizeof (st));
          tmp = message;
-
          res = mbsrtowcs(wmessage, &tmp, len, &st);
          if (res != len)
-         {
             break;
-         }
 
          if (__builtin_expect(len >= SIZE_MAX / 2, 0))
          {
@@ -517,7 +532,7 @@ report_error_tail(int status, int errnum, const char *message, va_list args)
             break;
          }
 
-            len *= 2;
+         len *= 2;
       }
 
       if (res == (size_t)-1)
@@ -532,49 +547,37 @@ report_error_tail(int status, int errnum, const char *message, va_list args)
       }
 
       __vfwprintf(stderr, wmessage, args);
-      if (log != NULL)
-      {
-         __vfwprintf(log, wmessage, arg2);
-      }
+      for (log_t *log = job_logs; log; log = log->next_log)
+         __vfwprintf(log->file, wmessage, arg2);
       
       if (use_malloc)
-      {
          free(wmessage);
-      }
    }
    else
 #endif
    {
+      // Write the message using standard chars.
       vfprintf(stderr, message, args);
-      if (log != NULL)
-      {
-         vfprintf(log, message, arg2);
-      }
+      for (log_t *log = job_logs; log; log = log->next_log)
+         vfprintf(log->file, message, arg2);
    }
 
   va_end(args);
   
    ++error_message_count;
    if (errnum)
-   {
-      report_errno_message(errnum);
-   }
-   
+      append_system_error_message(errnum);
+
+   // Finish off the error message with a newline.   
 #if _LIBC
-      __fxprintf(NULL, "\n");
+   __fxprintf(NULL, "\n");
 #else
    putc('\n', stderr);
-   if (log != NULL)
-   {
-      putc('\n', log);
-      fflush(log);
-   }
 #endif
-
    fflush(stderr);
-   if(status)
-   {
-      usage(status); 
+   for (log_t *log = job_logs; log; log = log->next_log) {
+         putc('\n', log->file);
+         fflush(log->file);
    }
 }
 
@@ -583,98 +586,42 @@ report_error(int status, int errnum, const char *message, ...)
 {
    pthread_mutex_lock(&reporting_lock);
 
-#if defined _LIBC && defined __libc_ptf_call
+   // Disable thread cancellation and lock stderr.
+#ifdef _LIBC
+  #ifdef __libc_ptf_call
    int state = PTHREAD_CANCEL_ENABLE;
    __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state), 0);
-#endif
-
-   fflush (stdout);
-   
-#ifdef _LIBC
+  #endif
    _IO_flockfile (stderr);
 #endif
+
+   // Prefix the error message with an attention grabbing character sequence.
 #if _LIBC
    __fxprintf (NULL, "%s[!!] ", progress_displayed ? "\n" : "");
 #else
    fprintf (stderr, "%s[!!] ", progress_displayed ? "\n" : "");
 #endif
-
-   if (log)
-   {
-      fputs("[!!] ", log);
-   }
+   write_to_logs("[!!] ", JOB_LOGS); 
 
    va_list args;
    va_start(args, message);
-   report_error_tail(status, errnum, message, args);
+   write_error_message(errnum, message, args);
 
+   // Enable thread cancellation and unlock stderr.
 #ifdef _LIBC
    _IO_funlockfile (stderr);
-# ifdef __libc_ptf_call
+  # ifdef __libc_ptf_call
    __libc_ptf_call (pthread_setcancelstate, (state, NULL), 0);
+  #endif
 #endif
-#endif
-
-   pthread_mutex_unlock(&reporting_lock);
-}
-
-// End code copied (and modified) from ../lib/error.c 
-
-static void
-report_to_log(const char* message)
-{
-   pthread_mutex_lock(&reporting_lock);
-
-   // Write the message to the console and to the log, if it exists.
-   fputs(message, stderr);   
-   if (log)
-   {
-      fputs(message, log);
-   }
 
    pthread_mutex_unlock(&reporting_lock);
-}
-
-static void
-report_to_hash_log_or_log(char* message)
-{
-   pthread_mutex_lock(&reporting_lock);
-
-   // Write the message to the console, and to the hash log, if it exists.
-   // If the hash log does not exist, write the message to the log,
-   // if it exists.
-   fputs(message, stderr);   
-   if (hash_log)
-   {
-      fputs(message, hash_log);
-   }
-   else if (log)
-   {
-      fputs(message, log);
-   }
 
-   pthread_mutex_unlock(&reporting_lock);
+   if (status)
+      usage(status); 
 }
 
-static void
-report_to_all(char* message)
-{
-   pthread_mutex_lock(&reporting_lock);
-
-   // Write the message to the console, and to the log and hash log,
-   // if they exist.
-   fputs(message, stderr);   
-   if (log)
-   {
-      fputs(message, log);
-   }
-   if (hash_log)
-   {
-      fputs(message, hash_log);
-   }
-
-   pthread_mutex_unlock(&reporting_lock);
-}
+// End code copied (and modified) from ../lib/error.c 
 
 static char*
 get_formatted_time_string()
@@ -684,17 +631,14 @@ get_formatted_time_string()
    struct tm tm;
    struct tm* ret = localtime_r(&t, &tm);
    if (ret == NULL)
-   {
       report_error(DC3DD_EXIT_ABORTED, errno, "localtime() failed");
-   }
 
    // Put it in string form.
    const size_t len = 32; // More than enough to hold 'YYYY-MM-DD HH:MM:SS -0000'
    char* time_str = (char*)malloc(len);
    if (strftime(time_str, len, "%F %T %z", &tm) == 0)
-   {
       report_error(DC3DD_EXIT_ABORTED, 0, "strftime() returned 0");
-   }
+
    return time_str;
 }
 
@@ -703,8 +647,7 @@ report_exit_message(int exit_code)
 {
    // Translate the exit code into a printable word.
    const char* verb = NULL;
-   switch (exit_code)
-   {
+   switch (exit_code) {
       case DC3DD_EXIT_COMPLETED:
           verb = _("completed");
           break;
@@ -718,13 +661,12 @@ report_exit_message(int exit_code)
           break;
    }
 
-   // Write the exit message to all logs (i.e., console, log, hash log).
-   // The message acts as a sort of footer for the run.
+   // Write the exit message to all logs as a sort of footer for the run.
    char* formatted_stop_time = get_formatted_time_string();
    char message[DISPLAY_MESSAGE_LENGTH];
    sprintf(message, _("%s %s at %s\n\n"), PROGRAM_NAME, verb, formatted_stop_time);
    free(formatted_stop_time);
-   report_to_all(message);
+   report(message, ALL_LOGS);
    flush_logs();
 }
 
@@ -733,7 +675,7 @@ report_program_error(const char* assertion)
 {
    char internal_error[DISPLAY_MESSAGE_LENGTH];
    sprintf(internal_error, _("%s: internal error %s at line %d"), program_name, assertion, __LINE__);
-   report_to_log(internal_error);
+   write_to_logs(internal_error, JOB_LOGS);
    report_exit_message(DC3DD_EXIT_ABORTED);
    terminate_logging();
    emit_bug_reporting_address();
@@ -743,31 +685,27 @@ report_program_error(const char* assertion)
 static void
 report_output_hashes(output_t* output)
 {
-   // Report the parallel hash lists stashed in the output struct.
    char message[DISPLAY_MESSAGE_LENGTH];
-   output_t* actual_hash_output = output->actual_hashes;
-   output_t* expected_hash_output = output->expected_hashes;
-   while (actual_hash_output && expected_hash_output)
-   { 
-      hash_t* actual_hash = actual_hash_output->hash;
-      hash_t* expected_hash = expected_hash_output->hash;
 
-      // Report total hash match/mismatch.
+   // Report the parallel hash lists stashed in the output struct.
+   hash_output_t *actual_hash = output->file->actual_hashes;
+   hash_output_t *expected_hash = output->file->expected_hashes;
+   while (actual_hash && expected_hash) { 
+      // Report the verification hash match/mismatch.
       sprintf(message, _("   %s %s (%s)\n"), 
-	 STREQ(actual_hash->result, expected_hash->result) ?  _("[ok]") : _("[MISMATCH]"),
-	 actual_hash->result,
+	 STREQ(actual_hash->total_hash->result, expected_hash->total_hash->result) 
+            ?  _("[ok]") : _("[MISMATCH]"),
+	 actual_hash->total_hash->result,
 	 actual_hash->algorithm->name);
-      report_to_all(message);
+      report(message, ALL_LOGS);
 
-     // Report piecewise hashes of files, if any.
-     if (output->files)
-     {
+     // Report piecewise hashes, if any.
+     if (output->file->files) {
 	uintmax_t start_sector = 0;
-	file_t* file = output->files;
-	hash_t* actual_piece = actual_hash->pieces;
-	hash_t* expected_piece = expected_hash->pieces;
-	while (file && actual_piece && expected_piece)         
-	{
+	file_t* file = output->file->files;
+	hash_t* actual_piece = actual_hash->piecewise_hash;
+	hash_t* expected_piece = expected_hash->piecewise_hash;
+	while (file && actual_piece && expected_piece) {
 	   // For a file set, report piecewise hash matches/mismatches,
 	   // indented two levels.
 	   sprintf(message, _("      %s %s, sectors %"PRIuMAX" - %"PRIuMAX", %s\n"), 
@@ -776,7 +714,7 @@ report_output_hashes(output_t* output)
 	      start_sector,
 	      start_sector + actual_piece->bytes_hashed / output->sector_size - 1,
 	      quote(file->name));
-	   report_to_hash_log_or_log(message);
+	   report(message, HASH_LOGS);
 
 	   start_sector += actual_piece->bytes_hashed / output->sector_size;
 	   file = file->next_file;
@@ -785,8 +723,34 @@ report_output_hashes(output_t* output)
 	}
      }
 
-     actual_hash_output = actual_hash_output->next_output;
-     expected_hash_output = expected_hash_output->next_output;
+     actual_hash = actual_hash->next;
+     expected_hash = expected_hash->next;
+   }
+
+   if (output->file->verification == DEVICE_FULL) {
+      hash_output_t *actual_hash = output->file->actual_hashes;
+      hash_output_t *expected_hash = output->file->expected_hashes;
+
+      // Compute the the number of bytes hashed beyond those that dc3dd wrote. 
+      uintmax_t additional_bytes = 
+         actual_hash->device_hash->bytes_hashed - actual_hash->total_hash->bytes_hashed;
+      uintmax_t sectors = additional_bytes / output->sector_size;
+      uintmax_t leftover_bytes = additional_bytes % output->sector_size;
+
+      // Write the results of the computation as a header for the additional hashes.
+      if (leftover_bytes == 0)
+         sprintf(message, _("   additional %"PRIuMAX" sectors of device hashed\n"), sectors);
+      else
+         sprintf(message, _("   additional %"PRIuMAX" sectors + %"PRIuMAX" bytes of device hashed\n "), 
+            sectors, leftover_bytes);
+      report(message, ALL_LOGS);
+
+      while (actual_hash && expected_hash) { 
+         sprintf(message, _("   %s (device total %s)\n"), actual_hash->device_hash->result, actual_hash->algorithm->name);
+         report(message, ALL_LOGS);
+         actual_hash = actual_hash->next;
+         expected_hash = expected_hash->next;
+      }
    }
 }
 
@@ -794,24 +758,21 @@ static void
 report_input_hashes(output_t* output)
 {
    char message[DISPLAY_MESSAGE_LENGTH];
+
+   // Report the verification hash.
    sprintf(message, _("   %s (%s)\n"),
-      output->hash->result, output->hash->algorithm->name);
-   report_to_all(message);
+      output->hash->total_hash->result, output->hash->algorithm->name);
+   report(message, ALL_LOGS);
 
-   if (output->hash->pieces != NULL)
-   {
-      uintmax_t start_sector = 0;
-      hash_t* piece = output->hash->pieces;
-      while (piece)
-      {
-         sprintf(message, _("      %s, sectors %"PRIuMAX" - %"PRIuMAX"\n"),
-            piece->result,
-            start_sector,
-            start_sector + piece->bytes_hashed / output->sector_size - 1);
-         report_to_hash_log_or_log(message);
-         start_sector += piece->bytes_hashed / output->sector_size;
-         piece = piece->next_hash;
-      }
+   // Report any piecewise hashes.
+   uintmax_t start_sector = 0;
+   for (hash_t *piece = output->hash->piecewise_hash; piece; piece = piece->next_hash) {
+      sprintf(message, _("      %s, sectors %"PRIuMAX" - %"PRIuMAX"\n"),
+         piece->result,
+         start_sector,
+         start_sector + piece->bytes_hashed / output->sector_size - 1);
+      report(message, HASH_LOGS);
+      start_sector += piece->bytes_hashed / output->sector_size;
    }
 }
 
@@ -819,90 +780,78 @@ static void
 report_files_IO(file_t* files, size_t sector_size, bool is_input)
 {
    char message[DISPLAY_MESSAGE_LENGTH];
-   file_t* file = files;
-   while (file)
-   {
-      uintmax_t sectors = file->size_in_bytes / sector_size;
-      uintmax_t leftover_bytes = file->size_in_bytes % sector_size;
+
+   for (file_t *file = files; file; file = file->next_file) {
+      uintmax_t sectors = file->bytes_processed / sector_size;
+      uintmax_t leftover_bytes = file->bytes_processed % sector_size;
       if (leftover_bytes == 0)
-      {
 	 sprintf(message,
              _("      %"PRIuMAX" sectors %s %s\n"),
             sectors, is_input ? _("in from") : _("out to"),
             quote(file->name));
-      } 
       else
-      {
 	 sprintf(message,
              _("      %"PRIuMAX" sectors + %"PRIuMAX" bytes %s %s\n"), 
 	    sectors, leftover_bytes, 
             is_input ?  _("in from") : _("out to"), quote(file->name));
-      }
-      report_to_log(message);
-      file = file->next_file;
+      report(message, JOB_LOGS);
    }
 }
 
 static void
 report_file_IO(file_t* file, uintmax_t bytes, size_t sector_size, bool is_input)
 {
-   // Write the file name.
    char message[DISPLAY_MESSAGE_LENGTH];
+
+   // Write the file name.
    sprintf(message, "%s results for %s %s:\n",
       is_input ? _("input") : _("output"),
       file->is_device ? _("device") : file->part_of_set ? _("files") : _("file"),
       quote(file->unparsed_name));   
-   report_to_all(message);
+   report(message, ALL_LOGS);
    
    // Write the number of the sectors read or written.
    uintmax_t sectors = bytes / sector_size;
    uintmax_t leftover_bytes = bytes % sector_size;
    if (leftover_bytes == 0)
-   {
       sprintf(message, _("   %"PRIuMAX" sectors %s\n"), sectors, is_input ? _("in") : _("out"));
-   } 
    else
-   {
       sprintf(message, _("   %"PRIuMAX" sectors + %"PRIuMAX" bytes %s\n"), 
          sectors, leftover_bytes, is_input ? _("in") : _("out"));
-   }
-   report_to_log(message);
+   report(message, JOB_LOGS);
 }
 
 static void 
 report_file_output(output_t* output)
 {
-   report_file_IO(output->current_file, output->bytes_output, output->sector_size, false);
+   file_output_t *file_output = output->file;
+   report_file_IO(file_output->current_file, file_output->bytes_output, output->sector_size, false);
    if (verbose_reporting)
-   {
-      report_files_IO(output->files, output->sector_size, false);
-   }
+      report_files_IO(file_output->files, output->sector_size, false);
 }
 
 static void 
 report_input(input_t* input)
 {
    char message[DISPLAY_MESSAGE_LENGTH];   
-   if (input->current_file)
-   {
+   if (input->current_file) {
+      // Report file input stats.
       report_file_IO(input->current_file, input->bytes_input, input->sector_size, true);
-      if (input->current_file->is_device)
-      {
+
+      if (input->current_file->is_device) {
 	 sprintf(message, _("   %"PRIuMAX" bad sectors replaced by zeros\n"), input->bad_sectors);
-	 report_to_log(message);
+	 report(message, JOB_LOGS);
       }
 
       if (verbose_reporting)
-      {
          report_files_IO(input->files, input->sector_size, true);
-      }
    }
-   else
-   {
+   else {
+      // Report pattern input stats.
       sprintf(message, _("input results for pattern %s:\n"), quote(input->pattern_string));
-      report_to_all(message);
+      report(message, ALL_LOGS);
       sprintf(message, _("   %"PRIuMAX" sectors in\n"), input->bytes_input / input->sector_size); 
-      report_to_all(message);
+      report(message, ALL_LOGS);
    }
 }
 
@@ -923,41 +872,31 @@ report_results(job_t* jobs)
    // as well as the console.
    imaging_job->report_progress(imaging_job, true);
    if (verification_job)
-   {
       verification_job->report_progress(verification_job, true);
-   }
-   if (!compact_reporting) report_to_all("\n");
+   if (!compact_reporting) report("\n", ALL_LOGS);
 
    // Report input stats and hashes.
    report_input(imaging_task->input);   
-   output_t* output = imaging_task->outputs;
-   while (output)
-   {
+   for (output_t* output = imaging_task->outputs;  output; output = output->next_output)
       if (output->hash)
-      {
 	 report_input_hashes(output);
-      }
-      output = output->next_output;
-   }   
-   if (!compact_reporting) report_to_all("\n");
+   if (!compact_reporting)
+      report("\n", ALL_LOGS);
 
    // Report output stats and hashes. 
-   output = imaging_task->outputs;
-   while (output)
-   {
-      if (output->current_file)
+   for (output_t* output = imaging_task->outputs;  output; output = output->next_output)
+      if (output->file)
       {
 	 report_file_output(output);
-	 if (output->current_file->verify_requested &&
+	 if (output->file->verification != NONE &&
 	     verification_job &&
 	     verification_job->exit_code == DC3DD_EXIT_COMPLETED)         
 	 {
 	    report_output_hashes(output);
 	 }             
-	 if (!compact_reporting) report_to_all("\n");
+	 if (!compact_reporting)
+            report("\n", ALL_LOGS);
       }
-      output = output->next_output;
-   }   
    
    pthread_mutex_unlock(&reporting_lock);
 }
@@ -965,38 +904,36 @@ report_results(job_t* jobs)
 static void
 report_verification_progress(job_t* job, bool final)
 {
+   // Calculate percent complete using the ratio of the bytes input for all of 
+   // the tasks to the bytes to be input for all of the tasks. Synchronization 
+   // of this read-only access to the task thread data is not required since 
+   // exact calculations are not required for the progress bar.
    uintmax_t bytes_input = 0;
    uintmax_t bytes_to_input = 0;
-   task_t* task = job->tasks;
-   while (task)
-   {
-      // Synchronization of this read-only access to the task thread data is not required
-      // since exact calculations are not required for the progress bar.
+   for (task_t* task = job->tasks; task; task = task->next_task) {
       bytes_input += task->input->bytes_input;
       bytes_to_input += task->input->bytes_to_input;
-      task = task->next_task;
    }
    float percent_complete = bytes_to_input ? 100.0f * (float)bytes_input / (float)bytes_to_input : 100.0f;
 
    pthread_mutex_lock(&reporting_lock);
+
    fprintf(stderr, "%79s", "\r");
-   if (bytes_to_input > 0)
-   {
+   if (bytes_to_input > 0) {
       fprintf(stderr, _("output hashing (%2.0f%%)%s"), percent_complete, final ? "\n" : "\r");
-      if (log && final)
-      {
-         fprintf(log, _("output hashing (%2.0f%%)\n"), percent_complete);
-      }
+      if (final) 
+         for (log_t* log = job_logs; log; log = log->next_log)
+            fprintf(log->file, _("output hashing (%2.0f%%)\n"), percent_complete);
    }
-   else
-   {
+   else {
       fprintf(stderr, _("output hashing (??%%)%s"), final ? "\n" : "\r");
-      if (log && final)
-      {
-         fputs(_("output hashing (??%%)\n"), log);
-      }
+      if (final)
+         for (log_t *log = job_logs; log; log = log->next_log)
+            fputs(_("output hashing (??%%)\n"), log->file);
    }
+
    progress_displayed = true;
+
    pthread_mutex_unlock(&reporting_lock);
 }
 
@@ -1018,36 +955,28 @@ report_imaging_progress(job_t* job, bool final)
       task->input->bytes_input,
       human_readable(task->input->bytes_input, hbuf, human_opts, 1, 1));
    fputs(stats, stderr);
-   if (log && final)
-   {
-      fputs(stats, log);
-   }
+   if (final)
+      write_to_logs(stats, JOB_LOGS);
    
-   if (task->input->bytes_to_input != INFINITE_BYTES)
-   {
+   if (task->input->bytes_to_input != INFINITE_BYTES) {
       // Oddly, the %% format specifier has no effect if this is done with sprintf(). 
       float percent_complete =
          100.0f * ((float)task->input->bytes_input / (float)task->input->bytes_to_input);
       fprintf(stderr, " (%2.0f%%)", percent_complete);   
-      if (log && final)
-      {
-         fprintf(log, " (%2.0f%%)", percent_complete);
-      }
+      if (final)
+         for (log_t *log = job_logs; log; log = log->next_log)
+            fprintf(log->file, " (%2.0f%%)", percent_complete);
    }
-   else
-   {
+   else {
       fputs(" (??%)", stderr);   
-      if (log && final)
-      {
-         fputs(" (??%)", log);
-      }
+      if (final) 
+         write_to_logs(" (??%)", JOB_LOGS);
    }
 
    double delta_s = 0.0;
    char const *bytes_per_second = NULL;
    xtime_t now = gethrxtime();
-   if (start_time < now)
-   {
+   if (start_time < now) {
       double XTIME_PRECISIONe0 = XTIME_PRECISION;
       uintmax_t delta_xtime = now;
       delta_xtime -= start_time;
@@ -1055,8 +984,7 @@ report_imaging_progress(job_t* job, bool final)
       bytes_per_second =
          human_readable(task->input->bytes_input, hbuf, human_opts, XTIME_PRECISION, delta_xtime);
    }
-   else
-   {
+   else {
       delta_s = 0.0;
       bytes_per_second = _("Infinity B");
    }
@@ -1072,101 +1000,97 @@ report_imaging_progress(job_t* job, bool final)
    //  bug we now use SI symbols even though they're a bit more
    //  confusing in English.
    fprintf (stderr, _(", %g s, %s/s        %s"), delta_s, bytes_per_second, final ? "\n" : "\r");
-   if (log && final)
-   {
-      fprintf (log, _(", %g s, %s/s\n"), delta_s, bytes_per_second);
-   }
+   if (final)
+      for (log_t *log = job_logs; log; log = log->next_log)
+         fprintf (log->file, _(", %g s, %s/s\n"), delta_s, bytes_per_second);
 
    progress_displayed = true;
+
    pthread_mutex_unlock(&reporting_lock);
 }
 
 static void
-add_to_task_list(task_t** head, task_t* new_task)
+add_to_log_list(log_t **list, log_t *new_log)
 {
-   if (*head)
-   {
-      task_t* task = *head;
-      while (task)
-      {
-         if (task->next_task == NULL)
-         {
-            task->next_task = new_task;
+   if (*list) {
+      for (log_t *log = *list; log; log = log->next_log)
+         if (!log->next_log) {
+            log->next_log = new_log;
             break;
          }
-         task = task->next_task;
-      }
    }
    else
-   {
-      *head = new_task;
+      *list = new_log;
+}
+
+static void
+add_to_task_list(task_t **list, task_t *new_task)
+{
+   if (*list) {
+      for (task_t *task = *list; task; task = task->next_task)
+         if (!task->next_task) {
+            task->next_task = new_task;
+            break;
+         }
    }
+   else
+      *list = new_task;
 }
 
 static void
-add_to_output_list(output_t** head, output_t* new_output)
+add_to_output_list(output_t **list, output_t *new_output)
 {
-   if (*head)
-   {
-      output_t* output = *head;
-      while (output)
-      {
-         if (output->next_output == NULL)
-         {
+   if (*list) {
+      for (output_t *output = *list; output; output = output->next_output)
+         if (!output->next_output) {
             output->next_output = new_output;
             break;
          }
-         output = output->next_output;
-      }
    }
    else
-   {
-      *head = new_output;
+      *list = new_output;
+}
+
+static void
+add_to_hash_output_list(hash_output_t **list, hash_output_t *new_item)
+{
+   if (*list) {
+      for (hash_output_t *item = *list; item; item = item->next)
+         if (!item->next) {
+            item->next= new_item;
+            break;
+         }
    }
+   else
+      *list = new_item;
 }
 
 static void
-add_to_file_list(file_t** head, file_t* new_file)
+add_to_file_list(file_t **list, file_t *new_file)
 {
-   if (*head)
-   {
-      file_t* file = *head;
-      while (file)
-      {
-         if (file->next_file == NULL)
-         {
+   if (*list) {
+      for (file_t *file = *list; file; file = file->next_file)
+         if (!file->next_file) {
             file->next_file = new_file;
             break;
          }
-         file = file->next_file;
-      }
    }
    else
-   {
-      *head = new_file;
-   }
+      *list = new_file;
 }
 
 static void
-add_to_hash_list(hash_t** head, hash_t* new_hash)
+add_to_hash_list(hash_t **list, hash_t *new_hash)
 {
-   if (*head)
-   {
-      hash_t* hash = *head;
-      while (hash)
-      {
-         if (hash->next_hash == NULL)
-         {
+   if (*list) {
+      for (hash_t *hash = *list; hash; hash = hash->next_hash)
+         if (!hash->next_hash) {
             hash->next_hash = new_hash;
             break;
          }
-         hash = hash->next_hash;
-      }
    }
    else
-   {
-      *head = new_hash;
-   }
+      *list = new_hash;
 }
 
 static void
@@ -1290,30 +1214,32 @@ generate_file_name(const char* unparsed_name, uint file_number)
 }
 
 static file_t*
-make_file(const char* name, int number, int flags, bool part_of_set, bool verify_requested)
+make_file(const char *name, int number, int flags, bool part_of_set, enum VERIFICATION_TYPE verification)
 {
-   file_t* file = NULL;
-   char* file_name = part_of_set ? generate_file_name(name, number) : strdup(name);
-   if (file_name)
+   file_t *file = NULL;
+
+   char *file_name = part_of_set ? generate_file_name(name, number) : strdup(name);
+   if (file_name) 
    {
       file = (file_t*)malloc(sizeof(file_t));
       file->unparsed_name = strdup(name);
       file->name = file_name;
+      file->part_of_set = part_of_set;
       file->number = number;
       file->flags = flags;
       file->descriptor = FILE_DESCRIPTOR_NOT_SET;
       file->offset = 0;
-      file->is_device = false;
-      file->is_block_device = false;
-      file->size_in_bytes = 0;
       file->probed = false;
       file->probed_size_in_bytes = 0;
       file->probed_size_in_sectors = 0;
       file->probed_sector_size = 0;
-      file->part_of_set= part_of_set;
-      file->verify_requested= verify_requested;
+      file->is_device = false;
+      file->is_block_device = false;
+      file->bytes_processed = 0;
+      file->verification = verification;
       file->next_file = NULL;
    }
+
    return file;
 }
 
@@ -1322,30 +1248,23 @@ close_file_output(output_t* output)
 {
    pthread_mutex_destroy(output->buffer_queue->lock);
 
-   if (output->current_file->descriptor >=0)
-   {
-      if (output->append_garbage_bytes)
-      {
+   file_output_t *file = output->file;
+   if (file->current_file->descriptor >=0) {
+      if (file->append_garbage_bytes) {
          memset(output->buffer_queue->buffers[0].data, '\0', output->buffer_size);
-         ssize_t bytes_written = write(output->current_file->descriptor,
+         ssize_t bytes_written = write(file->current_file->descriptor,
             output->buffer_queue->buffers[0].data,  output->buffer_size);
          if (bytes_written <= 0)
-         {
-            report_error(0, errno,  _("corrupting %s"), quote(output->current_file->name));
-         }
+            report_error(0, errno,  _("corrupting %s"), quote(file->current_file->name));
       }
 
-      if (close(output->current_file->descriptor) == 0)
-      {
+      if (close(file->current_file->descriptor) == 0) {
          if (output->state != FATAL_ERROR)
-         {
             // If not already in an error state, the output was completed.
             output->state = COMPLETE;
-         }
       }
-      else
-      {
-         report_error(0, errno, _("closing %s"), quote(output->current_file->name));
+      else {
+         report_error(0, errno, _("closing %s"), quote(file->current_file->name));
          output->state = FATAL_ERROR;
       }
    }
@@ -1354,40 +1273,35 @@ close_file_output(output_t* output)
 static void
 open_next_output_file(output_t* output)
 {
-   if (close(output->current_file->descriptor) == 0)
-   {
-      output->current_file->descriptor = FILE_DESCRIPTOR_NOT_SET;
+   file_output_t *file = output->file;
+   if (close(file->current_file->descriptor) == 0) {
+      file->current_file->descriptor = FILE_DESCRIPTOR_NOT_SET;
       
       file_t* next_file = make_file(
-         output->current_file->unparsed_name, 
-         output->current_file->number + 1, 
-         output->current_file->flags, 
+         file->current_file->unparsed_name, 
+         file->current_file->number + 1, 
+         file->current_file->flags, 
          true,
-         output->current_file->verify_requested);
-      if (next_file)
-      {
+         file->current_file->verification);
+      if (next_file) {
          next_file->descriptor = open(next_file->name, next_file->flags, OUTPUT_FILE_PERMS);
-         if (next_file->descriptor >= 0)
-         {
-            output->current_file = next_file;
-            add_to_file_list(&output->files, next_file);
+         if (next_file->descriptor >= 0) {
+            file->current_file = next_file;
+            add_to_file_list(&file->files, next_file);
          }
-         else
-         {
+         else {
             report_error(0, errno, _("opening %s"), quote(next_file->name));
             output->state = FATAL_ERROR;
          }
       }
-      else
-      {
-         report_error(0, 0 , _("file extensions exhausted for %s"), output->current_file->unparsed_name);
+      else {
+         report_error(0, 0 , _("file extensions exhausted for %s"), file->current_file->unparsed_name);
          output->state = FATAL_ERROR;
       }
    }
-   else
-   {
-      output->current_file->descriptor = FILE_DESCRIPTOR_NOT_SET;
-      report_error(0, errno, _("closing %s"), quote(output->current_file->name));
+   else {
+      file->current_file->descriptor = FILE_DESCRIPTOR_NOT_SET;
+      report_error(0, errno, _("closing %s"), quote(file->current_file->name));
       output->state = FATAL_ERROR;
    }
 }
@@ -1397,10 +1311,11 @@ write_bytes_to_file(output_t* output, char const *buffer, size_t bytes_to_write)
 {
    size_t total_bytes_written = 0;
 
+   file_output_t *file = output->file;
    while (total_bytes_written < bytes_to_write)
    {
       ssize_t bytes_written = write(
-         output->current_file->descriptor, 
+         file->current_file->descriptor, 
          buffer + total_bytes_written, 
          bytes_to_write - total_bytes_written);
          
@@ -1408,7 +1323,7 @@ write_bytes_to_file(output_t* output, char const *buffer, size_t bytes_to_write)
       {
          if (errno != EINTR)
          {
-            report_error(0, errno, _("writing to %s"), quote(output->current_file->name));
+            report_error(0, errno, _("writing to %s"), quote(file->current_file->name));
             output->state = FATAL_ERROR;
             break;
          }
@@ -1419,14 +1334,14 @@ write_bytes_to_file(output_t* output, char const *buffer, size_t bytes_to_write)
          // a device's end.  (Example: Linux 1.2.13 on /dev/fd0.)
          // Set errno to ENOSPC for a sensible diagnostic. 
          errno = ENOSPC;
-         report_error(0, errno, _("writing to %s"), quote(output->current_file->name));
+         report_error(0, errno, _("writing to %s"), quote(file->current_file->name));
          output->state = FATAL_ERROR;
          break;
       }
       else
       {
          total_bytes_written += (size_t)bytes_written;
-         output->current_file->size_in_bytes += (size_t)total_bytes_written;
+         file->current_file->bytes_processed += (size_t)total_bytes_written;
       }
    }
 
@@ -1438,7 +1353,8 @@ write_bytes_to_files(output_t* output, char const *buffer, size_t bytes_to_write
 {
    size_t bytes_written = 0;
 
-   intmax_t bytes_left_for_file = output->max_file_size - output->current_file->size_in_bytes;
+   file_output_t *file = output->file;
+   intmax_t bytes_left_for_file = file->max_file_size - file->current_file->bytes_processed;
    if (bytes_to_write <= bytes_left_for_file)
    {
       // Write all of the bytes in the buffer to the current file.
@@ -1467,42 +1383,36 @@ write_bytes_to_files(output_t* output, char const *buffer, size_t bytes_to_write
 static void
 write_bytes_to_image(output_t* output, buffer_t* buffer)
 {
-   if (output->current_file->part_of_set)
-   {
-      output->bytes_output += write_bytes_to_files(output, buffer->data, buffer->length);  
-   }
+   if (output->file->current_file->part_of_set)
+      output->file->bytes_output += write_bytes_to_files(output, buffer->data, buffer->length);  
    else
-   {
-      output->bytes_output += write_bytes_to_file(output, buffer->data, buffer->length);
-   }
+      output->file->bytes_output += write_bytes_to_file(output, buffer->data, buffer->length);
 }
 
 static ssize_t
 read_bytes(int file_descriptor, char *buffer, size_t bytes_to_read)
 {
-   for (;;)
-   {
+   for (;;) {
       ssize_t bytes_read;
       bytes_read = read(file_descriptor, buffer, bytes_to_read);
       if (!(bytes_read < 0 && errno == EINTR))
-      {
          return bytes_read;
-      }
    }
 }
 
 static void
 skip_output_sectors(output_t* output)
 {
-   if (output->sectors_to_skip > 0)
+   file_output_t *file = output->file;
+   if (file->sectors_to_skip > 0)
    {
-      uintmax_t bytes_to_skip = output->sectors_to_skip * output->sector_size;
+      uintmax_t bytes_to_skip = file->sectors_to_skip * output->sector_size;
       if (bytes_to_skip <= OFF_T_MAX) 
       {
-         if (lseek(output->current_file->descriptor, bytes_to_skip, SEEK_CUR) < 0)
+         if (lseek(file->current_file->descriptor, bytes_to_skip, SEEK_CUR) < 0)
          {
 	    report_error(0, errno, _("lseek() on %s failed while skipping sectors"),
-	       quote(output->current_file->name));
+	       quote(file->current_file->name));
 	    output->state = FATAL_ERROR;
          }
       }
@@ -1516,7 +1426,7 @@ skip_output_sectors(output_t* output)
 	    size_t bytes_to_read =
 	       bytes_to_skip >= output->buffer_size ? output->buffer_size : bytes_to_skip; 
 	    ssize_t bytes_read = 
-	       read_bytes(output->current_file->descriptor, buffer, bytes_to_read);
+	       read_bytes(file->current_file->descriptor, buffer, bytes_to_read);
 	    if (bytes_read > 0)
 	    {
 	       bytes_to_skip -= bytes_read;
@@ -1525,7 +1435,7 @@ skip_output_sectors(output_t* output)
 	    {
 	       char message[DISPLAY_MESSAGE_LENGTH];
 	       sprintf(message,  _("encountered end of file reading %s to skip sectors"),
-		  quote(output->current_file->name));
+		  quote(file->current_file->name));
 	       report_error(0, 0, message); 
 	       output->state = FATAL_ERROR;         
 	       break;
@@ -1533,7 +1443,7 @@ skip_output_sectors(output_t* output)
 	    else
 	    {
 	      report_error(0, errno, _("reading %s  while skipping sectors"),
-		 quote(output->current_file->name));
+		 quote(file->current_file->name));
 	       output->state = FATAL_ERROR;
 	       break;
 	    }         
@@ -1546,18 +1456,19 @@ skip_output_sectors(output_t* output)
 static void
 open_file_output(output_t* output)
 {
-   pthread_mutex_init(output->buffer_queue->lock, NULL);
+   //pthread_mutex_init(output->buffer_queue->lock, NULL);
 
-   output->current_file->descriptor = 
-      open(output->current_file->name, output->current_file->flags, OUTPUT_FILE_PERMS); 
-   if (output->current_file->descriptor >= 0) 
+   file_output_t *file = output->file;
+   file->current_file->descriptor = 
+      open(file->current_file->name, file->current_file->flags, OUTPUT_FILE_PERMS); 
+   if (file->current_file->descriptor >= 0) 
    {
       output->state = OPEN;
       skip_output_sectors(output);      
    }
    else
    {
-      report_error(0, errno,_("opening %s"), quote(output->current_file->name));
+      report_error(0, errno,_("opening %s"), quote(file->current_file->name));
       output->state = FATAL_ERROR;
    }   
 }
@@ -1573,34 +1484,43 @@ static void
 connect_to_std_out(output_t* output)
 {
    pthread_mutex_init(output->buffer_queue->lock, NULL);
-   output->current_file->descriptor = STDOUT_FILENO;
+   output->file->current_file->descriptor = STDOUT_FILENO;
    output->state = OPEN;
 }
 
 static void
-get_hash_result(hash_t* hash)
+get_hash_result(hash_t* hash, size_t sum_size)
 {
    static char hex[] = "0123456789abcdef";
 
-   for (size_t p = 0; p < hash->algorithm->sum_size ; p++)
+   for (size_t p = 0; p < sum_size ; p++)
    {
       hash->result[2 * p] = hex[(hash->sum[p] >> 4) & 0xf];
       hash->result[2 * p + 1] = hex[hash->sum[p] & 0xf];
    }
 
-   hash->result[2 * hash->algorithm->sum_size] = 0;
+   hash->result[2 * sum_size] = 0;
 }
 
 static void
 close_hash(output_t* output)
 {
-   output->hash->algorithm->finish(output->hash->context, output->hash->sum);
-   get_hash_result(output->hash);
-   if (output->hash->current_piece != NULL)
-   {
-      output->hash->current_piece->algorithm->finish(output->hash->current_piece->context,
-         output->hash->current_piece->sum);
-      get_hash_result(output->hash->current_piece);
+   hash_output_t * hash = output->hash;
+
+   // Finish the total hash.
+   hash->algorithm->finish(hash->total_hash->context, hash->total_hash->sum);
+   get_hash_result(hash->total_hash, hash->algorithm->sum_size);
+
+   // Finish the piecewise hash.
+   if (hash->current_piece) {
+      hash->algorithm->finish(hash->current_piece->context, hash->current_piece->sum);
+      get_hash_result(hash->current_piece, hash->algorithm->sum_size);
+   }
+
+   // Finish the hash of the device that received the device.
+   if (hash->device_hash) {
+      hash->algorithm->finish(hash->device_hash->context, hash->device_hash->sum);
+      get_hash_result(hash->device_hash, hash->algorithm->sum_size);
    }
 
    pthread_mutex_destroy(output->buffer_queue->lock);
@@ -1611,15 +1531,11 @@ static hash_t*
 make_hash(hash_algorithm_t* algorithm)
 {
    hash_t* hash = (hash_t*)malloc(sizeof(hash_t));
-   hash->algorithm = algorithm;
    hash->context = malloc(algorithm->context_size);
    hash->sum = (char*)malloc(algorithm->sum_size);
    hash->result = (char*)malloc(2 * algorithm->sum_size + 1);
    hash->result[0] = 0;
    hash->bytes_hashed = 0;
-   hash->current_piece = NULL;
-   hash->pieces = NULL;
-   hash->piecewise_hash_length = 0;
    hash->next_hash = NULL;
    return hash;
 }
@@ -1632,23 +1548,23 @@ piecewise_hash_bytes(output_t* output, const char* buf, size_t buf_length)
 
    if (bytes_left_for_piece == 0)
    {
-      output->hash->current_piece->algorithm->finish(output->hash->current_piece->context,
+      output->hash->algorithm->finish(output->hash->current_piece->context,
          output->hash->current_piece->sum);
-      get_hash_result(output->hash->current_piece);
-      output->hash->current_piece = make_hash(output->hash->current_piece->algorithm);
-      add_to_hash_list(&output->hash->pieces, output->hash->current_piece);
-      output->hash->current_piece->algorithm->init(output->hash->current_piece->context);
+      get_hash_result(output->hash->current_piece, output->hash->algorithm->sum_size);
+      output->hash->current_piece = make_hash(output->hash->algorithm);
+      add_to_hash_list(&output->hash->piecewise_hash, output->hash->current_piece);
+      output->hash->algorithm->init(output->hash->current_piece->context);
       bytes_left_for_piece = output->hash->piecewise_hash_length;
    }
 
    if (buf_length <= bytes_left_for_piece)
    {
-      output->hash->current_piece->algorithm->update(buf, buf_length, output->hash->current_piece->context);
+      output->hash->algorithm->update(buf, buf_length, output->hash->current_piece->context);
       output->hash->current_piece->bytes_hashed += buf_length;
    }
    else
    {
-      output->hash->current_piece->algorithm->update(buf, bytes_left_for_piece,
+      output->hash->algorithm->update(buf, bytes_left_for_piece,
          output->hash->current_piece->context);
       output->hash->current_piece->bytes_hashed += bytes_left_for_piece;
       piecewise_hash_bytes(output, buf + bytes_left_for_piece, buf_length - bytes_left_for_piece);
@@ -1658,10 +1574,25 @@ piecewise_hash_bytes(output_t* output, const char* buf, size_t buf_length)
 static void
 hash_bytes(output_t* output, buffer_t* buffer)
 {
-   output->hash->algorithm->update(buffer->data, buffer->length, output->hash->context);
-   if (output->hash->current_piece != NULL)
-   {   
+   hash_output_t *hash = output->hash;
+   hash->algorithm->update(buffer->data, buffer->length, hash->total_hash->context);
+   hash->total_hash->bytes_hashed += buffer->length;
+   if (hash->current_piece)
       piecewise_hash_bytes(output, buffer->data, buffer->length);
+}
+
+static void
+hash_device_bytes(output_t* output, buffer_t* buffer)
+{
+   hash_output_t *hash = output->hash;
+   hash->algorithm->update(buffer->data, buffer->length, hash->device_hash->context);
+   hash->device_hash->bytes_hashed += buffer->length;
+   uintmax_t bytes_remaining = hash->total_hash_length - hash->total_hash->bytes_hashed;
+   if (bytes_remaining > 0) {
+      if (bytes_remaining < buffer->length)
+         buffer->length = bytes_remaining;
+      hash->algorithm->update(buffer->data, buffer->length, hash->total_hash->context);
+      hash->total_hash->bytes_hashed += buffer->length;
    }
 }
 
@@ -1670,11 +1601,14 @@ open_hash(output_t* output)
 {
    pthread_mutex_init(output->buffer_queue->lock, NULL);
 
-   output->hash->algorithm->init(output->hash->context);
-   if (output->hash->current_piece != NULL)
-   {
-      output->hash->current_piece->algorithm->init(output->hash->current_piece->context);
-   } 
+   hash_output_t *hash = output->hash;
+   hash->algorithm->init(output->hash->total_hash->context);
+
+   if (hash->current_piece != NULL)
+      hash->algorithm->init(hash->current_piece->context);
+
+   if (hash->device_hash != NULL)
+      hash->algorithm->init(hash->device_hash->context);
 
    output->state = OPEN;
 }
@@ -1684,13 +1618,20 @@ wait_for_buffer(buffer_queue_t* buffer_queue)
 {
    // This function is called by an output thread each time it
    // finishes consuming some input bytes furnished by an input (i.e., task) thread.
+
+   uint buffers_used = 0;
+
    pthread_mutex_lock(buffer_queue->lock);
    if (buffer_queue->buffers_used == 0 && !buffer_queue->done_buffering)
    {
       pthread_cond_wait(buffer_queue->not_empty, buffer_queue->lock);
    }   
+
+   buffers_used = buffer_queue->buffers_used;
+
    pthread_mutex_unlock(buffer_queue->lock);
-   return buffer_queue->buffers_used > 0;
+
+   return buffers_used > 0;
 }
 
 static void* 
@@ -1750,7 +1691,7 @@ advance_input(input_t* input, uintmax_t bytes_read)
    if (input->current_file)
    {
       input->current_file->offset += bytes_read;
-      input->current_file->size_in_bytes += bytes_read;
+      input->current_file->bytes_processed += bytes_read;
       input->current_sector += bytes_read / input->sector_size;
    }
    input->bytes_input += bytes_read;
@@ -2002,7 +1943,7 @@ open_next_input_file(input_t* input)
          input->current_file->number + 1, 
          input->current_file->flags, 
          true,
-         false);
+         input->current_file->verification);
 
       if (next_file)
       {
@@ -2060,7 +2001,7 @@ skip_device_input_sectors(input_t* input)
 
          // Correct bytes input, since bytes skipped, not input
          input->bytes_input -= input->sector_size;
-         input->current_file->size_in_bytes -= input->sector_size;
+         input->current_file->bytes_processed -= input->sector_size;
       }
       else if (bytes_read == 0 || errno == ENOSPC)
       {
@@ -2092,7 +2033,7 @@ skip_device_input_sectors(input_t* input)
             {
 	       // Correct bytes input, since bytes skipped, not input
 	       input->bytes_input -= input->sector_size;
-	       input->current_file->size_in_bytes -= input->sector_size;
+	       input->current_file->bytes_processed -= input->sector_size;
             } 
 	 }
 	 else
@@ -2124,7 +2065,7 @@ skip_file_input_sectors(input_t* input)
 
          // Correct bytes input, since bytes skipped, not input
          input->bytes_input -= bytes_read;
-         input->current_file->size_in_bytes -= bytes_read;
+         input->current_file->bytes_processed -= bytes_read;
 
          bytes_to_skip -= bytes_read;
       }
@@ -2162,18 +2103,13 @@ skip_input_sectors(input_t* input)
 
          // Correct bytes input, since bytes skipped, not input
          input->bytes_input -= bytes_to_skip;
-         input->current_file->size_in_bytes -= bytes_to_skip;
+         input->current_file->bytes_processed -= bytes_to_skip;
       }
-      else
-      {
+      else {
          if (input->current_file->is_device)
-         {
             skip_device_input_sectors(input);
-         }
          else
-         {
             skip_file_input_sectors(input);
-         }
       }
    }
 }
@@ -2332,11 +2268,9 @@ get_file_stats(file_t* file)
 static bool 
 probe_file(file_t* file)
 {
-   if (!file->probed)
-   {
+   if (!file->probed) {
       get_file_stats(file);
-      if (file->probed && file->part_of_set)
-      {
+      if (file->probed && file->part_of_set) {
 	 // Generate the set of potential file names and attempt to
 	 // open the set of files. Stop when file extensions are exhausted 
 	 // or the next file does not exist. Note that there is an assumption
@@ -2348,34 +2282,26 @@ probe_file(file_t* file)
 	 next_file.probed_size_in_bytes = 0;
 	 next_file.probed_sector_size = 0;
 	 next_file.name = generate_file_name(file->unparsed_name, ++next_file.number); 
-	 while (next_file.name)
-	 {
+	 while (next_file.name) {
 	    next_file.descriptor = open(next_file.name, O_RDONLY, 0);
-	    if (next_file.descriptor >= 0)
-	    {
+	    if (next_file.descriptor >= 0) {
 	       // This is another file in the set, get its size.
 	       get_file_stats(&next_file);
 	       if (next_file.probed)
-	       {
 		  file->probed_size_in_bytes += next_file.probed_size_in_bytes;
-	       }
-	       else
-	       {
+	       else {
 		  report_error(0, 0, _("probe of %s failed"), quote(next_file.name));
 		  file->probed = false;
 	       }
 
-	       if (close(next_file.descriptor) != 0)
-	       {
+	       if (close(next_file.descriptor) != 0) {
 		  report_error(0, errno, _("closing %s after size probe"), quote(next_file.name));
 		  file->probed = false;
 	       }
 
 	       free(next_file.name);
                if (!file->probed)
-               {
                   break;
-               }
 	    }
 	    else
 	    {
@@ -2395,30 +2321,29 @@ static void
 open_file_input(input_t* input)
 {
    input->current_file->descriptor = open(input->current_file->name, input->current_file->flags, 0); 
-   if (input->current_file->descriptor >= 0)
-   {
-      if (probe_file(input->current_file))
-      {
+   if (input->current_file->descriptor >= 0) {
+      if (probe_file(input->current_file)) {
 	 input->state = OPEN;            
-	 input->bytes_to_input = input->current_file->probed_size_in_bytes; 
-	 if (input->max_sectors_to_input != INFINITE_SECTORS)
-	 {
-	    uintmax_t max_bytes_to_input = input->max_sectors_to_input * input->sector_size;
-	    if (max_bytes_to_input < input->bytes_to_input)
-	    {
-	       input->bytes_to_input = max_bytes_to_input;
+         
+         // If bytes to input is not pre-determined, calculate bytes to input
+         // from input size.
+         if (input->bytes_to_input == 0) {
+	    input->bytes_to_input = input->current_file->probed_size_in_bytes; 
+	    if (input->max_sectors_to_input != INFINITE_SECTORS) {
+	       uintmax_t max_bytes_to_input = input->max_sectors_to_input * input->sector_size;
+	       if (max_bytes_to_input < input->bytes_to_input)
+	          input->bytes_to_input = max_bytes_to_input;
 	    }
-	 }
+         }
+
 	 skip_input_sectors(input);      
       }
-      else
-      {
+      else {
 	 report_error(0, errno, _("probing %s for bytes to input"), quote(input->current_file->name)); 
 	 input->state = FATAL_ERROR;
       }
    }
-   else 
-   {
+   else {
       report_error(0, errno, _("opening %s"), quote(input->current_file->name)); 
       input->state = FATAL_ERROR;
    }
@@ -2471,7 +2396,7 @@ open_pattern_input(input_t* input)
 static void 
 set_exit_code(task_t* task)
 {
-   pthread_mutex_lock(task->signalling_lock);
+   pthread_mutex_lock(task->signaling_lock);
    task->completed = true;
    if (task->aborted)
    {
@@ -2500,7 +2425,7 @@ set_exit_code(task_t* task)
          }
       }
    }
-   pthread_mutex_unlock(task->signalling_lock);
+   pthread_mutex_unlock(task->signaling_lock);
 }
 
 static void
@@ -2539,48 +2464,41 @@ wait_for_output_threads(task_t* task)
 static void
 produce_bytes(task_t* task)
 {
-   input_t* input = task->input;
-         // Produce an input buffer and copy it to each output buffer queue.
-         input->produce_bytes(input); 
-         if (input->buffer.length > 0)
-         {
-            output_t* output = task->outputs;
-            while (output)
-            {
-	       // Wait for an empty buffer in the buffer buffer queue for this
-	       // output.
-               buffer_queue_t* buffer_queue = output->buffer_queue;
-	       pthread_mutex_lock(buffer_queue->lock);
-	       if (buffer_queue->buffers_used == buffer_queue->number_of_buffers)
-	       {
-		  pthread_cond_wait(buffer_queue->not_full, buffer_queue->lock);
-	       }
-	       pthread_mutex_unlock(buffer_queue->lock);
-
-	       // Copy the input buffer into the buffer queue. 
-	       memcpy(buffer_queue->buffers[buffer_queue->next_available_buffer].data,
-		  input->buffer.data, input->buffer.length);
-	       buffer_queue->buffers[buffer_queue->next_available_buffer].length = input->buffer.length;
-
-	       // Notify the output thread that another buffer is available.
-	       // This will release the output thread if it is blocked waiting
-	       // for bytes to output.
-	       pthread_mutex_lock(buffer_queue->lock);
-	       buffer_queue->next_available_buffer = 
-		  (buffer_queue->next_available_buffer + 1) % buffer_queue->number_of_buffers;
-	       ++buffer_queue->buffers_used;
-	       pthread_cond_signal(buffer_queue->not_empty);
-	       pthread_mutex_unlock(buffer_queue->lock);
-
-               output = output->next_output;
-            }
-         }
+   // Produce an input buffer and copy it to each output buffer queue.
+   input_t *input = task->input;
+   input->produce_bytes(input); 
+   if (input->buffer.length > 0) {
+      for (output_t *output = task->outputs; output; output = output->next_output) {
+         // Wait for an empty buffer in the buffer queue for this
+         // output.
+         buffer_queue_t *buffer_queue = output->buffer_queue;
+         pthread_mutex_lock(buffer_queue->lock);
+         if (buffer_queue->buffers_used == buffer_queue->number_of_buffers)
+            pthread_cond_wait(buffer_queue->not_full, buffer_queue->lock);
+         pthread_mutex_unlock(buffer_queue->lock);
+
+         // Copy the input buffer into the buffer queue. 
+         memcpy(buffer_queue->buffers[buffer_queue->next_available_buffer].data,
+            input->buffer.data, input->buffer.length);
+         buffer_queue->buffers[buffer_queue->next_available_buffer].length = input->buffer.length;
+
+         // Notify the output thread that another buffer is available.
+         // This will release the output thread if it is blocked waiting
+         // for bytes to output.
+         pthread_mutex_lock(buffer_queue->lock);
+         buffer_queue->next_available_buffer = 
+            (buffer_queue->next_available_buffer + 1) % buffer_queue->number_of_buffers;
+         ++buffer_queue->buffers_used;
+         pthread_cond_signal(buffer_queue->not_empty);
+         pthread_mutex_unlock(buffer_queue->lock);
+      }
+   }
 }
 
 static bool
 is_task_completed(task_t* task)
 {
-   pthread_mutex_lock(task->signalling_lock);
+   pthread_mutex_lock(task->signaling_lock);
    
    // Check for task killed.
    task->completed = task->aborted;
@@ -2612,7 +2530,7 @@ is_task_completed(task_t* task)
       }
    }
 
-   pthread_mutex_unlock(task->signalling_lock);
+   pthread_mutex_unlock(task->signaling_lock);
    
    return task->completed;
 }
@@ -2657,17 +2575,30 @@ open_IO(task_t* task)
 }
 
 static void* 
-execute_task(void* arg)
-{
-   // This is the thread function for task (i.e., input) threads.
+execute_task(void* arg) {
    task_t* task = (task_t*)arg;
-   if (open_IO(task))
-   {
+
+   if (task->verification_target) {  
+      if (task->verification_target->verification == DEVICE_PARTIAL)
+      // If this task is a verification of only the bytes dc3dd wrote to a
+      // device, limit the size of the input to the number of bytes written
+      // to the verification target during imaging.
+      task->input->bytes_to_input = task->verification_target->bytes_output;
+
+      if (task->verification_target->verification == DEVICE_FULL)
+      // If this task is a verification of both the bytes dc3dd wrote to a
+      // device and the entire device, limit the total hash(es) to the number of
+      // bytes written to the verification target during imaging.
+      for (output_t *output = task->outputs; output; output = output->next_output) {
+         if (output->hash)
+            output->hash->total_hash_length = task->verification_target->bytes_output;
+      }
+   }
+
+   if (open_IO(task)) {
       start_output_threads(task);
-      while (!is_task_completed(task))
-      {
+      while (!is_task_completed(task)) 
          produce_bytes(task);
-      } 
       wait_for_output_threads(task);   
       close_IO(task);
    }
@@ -2676,36 +2607,25 @@ execute_task(void* arg)
 }
 
 static void
-abort_job(job_t* job)
-{
-   task_t* task = job->tasks;
-   while (task)
-   {
-      pthread_mutex_lock(task->signalling_lock);
+abort_job(job_t *job) {
+   for (task_t *task = job->tasks; task; task = task->next_task) {
+      pthread_mutex_lock(task->signaling_lock);
       task->aborted = true;
-      pthread_mutex_unlock(task->signalling_lock);         
-      task = task->next_task;
+      pthread_mutex_unlock(task->signaling_lock);         
    }
 }
 
 static bool 
-job_is_active(job_t* job)
-{
+job_is_active(job_t* job) {
    bool is_active = false;
    
-   task_t* task = job->tasks;
-   while (task)
-   {
-      pthread_mutex_lock(task->signalling_lock);
+   for (task_t *task = job->tasks; task; task = task->next_task) {
+      pthread_mutex_lock(task->signaling_lock);
       is_active = !task->completed && !task->aborted;
-      pthread_mutex_unlock(task->signalling_lock);               
+      pthread_mutex_unlock(task->signaling_lock);               
    
       if (is_active)
-      {
          break;
-      }
-      
-      task = task->next_task;
    }
    
    return is_active;
@@ -2735,10 +2655,10 @@ monitor_job(void *arg)
       
       // Wait until either the signal handling thread receives an interrupt or
       // it's time for another progress check and report. 
-      pthread_mutex_lock(&signalling_lock);
-      if (pthread_cond_timedwait(interrupted, &signalling_lock, &next_progress_check_time) == ETIMEDOUT)
+      pthread_mutex_lock(&signaling_lock);
+      if (pthread_cond_timedwait(interrupted, &signaling_lock, &next_progress_check_time) == ETIMEDOUT)
       {
-         pthread_mutex_unlock(&signalling_lock);      
+         pthread_mutex_unlock(&signaling_lock);      
          job->report_progress(job, false);
          if (!job_is_active(job))
          {
@@ -2769,9 +2689,9 @@ await_interrupt_signal(void* arg)
    sigaddset(&set, SIGINT);
    sigwait(&set, &sig);
    
-   pthread_mutex_lock(&signalling_lock);
+   pthread_mutex_lock(&signaling_lock);
    pthread_cond_signal(interrupted);
-   pthread_mutex_unlock(&signalling_lock);
+   pthread_mutex_unlock(&signaling_lock);
 
    pthread_exit(NULL);
 }
@@ -2784,7 +2704,7 @@ execute_job(job_t* job)
    task_t* task = job->tasks;
    while (task)
    {
-      pthread_mutex_init(task->signalling_lock, NULL);
+      pthread_mutex_init(task->signaling_lock, NULL);
       start_thread(&task->thread, execute_task, task);
       task = task->next_task;
    }
@@ -2797,7 +2717,7 @@ execute_job(job_t* job)
    while (task)
    {
       pthread_join(task->thread, NULL);
-      pthread_mutex_destroy(task->signalling_lock);
+      pthread_mutex_destroy(task->signaling_lock);
       task = task->next_task;
    }
 
@@ -2829,7 +2749,7 @@ execute_jobs(job_t* jobs)
    pthread_sigmask(SIG_BLOCK, &set, 0);
 
    // Set up synchronization for signal handling and kick off the signal handling thread.
-   pthread_mutex_init(&signalling_lock, NULL);      
+   pthread_mutex_init(&signaling_lock, NULL);      
    interrupted = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
    pthread_cond_init(interrupted, NULL);
    pthread_t signal_handling_thread;
@@ -2849,7 +2769,7 @@ execute_jobs(job_t* jobs)
 
    // Shut down the signal handling thread and tear down synchronization.      
    pthread_cancel(signal_handling_thread);
-   pthread_mutex_destroy(&signalling_lock);
+   pthread_mutex_destroy(&signaling_lock);
    
    return exit_code;
 }
@@ -2867,12 +2787,13 @@ make_job(task_t* tasks, void (*report_progress)(job_t*, bool))
 }
 
 static task_t*
-make_task(input_t* input, output_t* outputs)
+make_task(input_t *input, output_t *outputs, file_output_t *verification_target)
 {
-   task_t* task = (task_t*)malloc(sizeof(task_t)); 
-   task->signalling_lock = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
+   task_t *task = (task_t*)malloc(sizeof(task_t)); 
+   task->signaling_lock = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
    task->input = input;   
    task->outputs = outputs;
+   task->verification_target = verification_target;
    task->completed = false;
    task->aborted = false;
    task->exit_code = DC3DD_EXIT_CODE_NOT_SET;
@@ -2893,12 +2814,18 @@ make_buffer_queue(size_t size_in_bytes, size_t number_of_buffers)
    buffer_queue->number_of_buffers = number_of_buffers;
    buffer_queue->buffers_used = 0;
    buffer_queue->next_available_buffer = 0;
+
    buffer_queue->lock = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
+   pthread_mutex_init(buffer_queue->lock, NULL);
+
    buffer_queue->not_empty = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
    pthread_cond_init(buffer_queue->not_empty, NULL);
+
    buffer_queue->not_full = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
    pthread_cond_init(buffer_queue->not_full,  NULL);
+
    buffer_queue->done_buffering = false;
+
    return buffer_queue;
 }
 
@@ -2910,86 +2837,100 @@ make_output(settings_t* settings)
    output->sector_size = settings->sector_size;
    output->buffer_size = settings->buffer_size;
    output->buffer_queue = make_buffer_queue(output->buffer_size, NUM_BUFFERS);
-   output->bytes_output = 0;
    output->hash = NULL;
-   output->current_file = NULL;
-   output->files = NULL;
-   output->max_file_size = settings->max_output_file_size;
-   output->sectors_to_skip = settings->output_sectors_to_skip;
-   output->append_garbage_bytes = settings->corrupt_output;
+   output->file = NULL;
    output->open = NULL;
    output->consume_bytes = NULL;
    output->close = NULL;
-   output->expected_hashes = NULL;
-   output->actual_hashes = NULL;
    output->next_output = NULL;
    return output;
 }
 
 static output_t*
-make_file_output(settings_t* settings, file_t* file, output_t* expected_hashes)
+make_file_output(settings_t* settings, file_t* file, hash_output_t* expected_hashes)
 {
-   output_t* output = make_output(settings);
-   output->current_file = file;
-   output->files = output->current_file->part_of_set? file : NULL;
    if (settings->append_output || settings->output_sectors_to_skip)
-   {
-      output->current_file->flags |= O_APPEND;
-   }
+      file->flags |= O_APPEND;
    else
-   {
-      output->current_file->flags |= (O_CREAT | O_TRUNC);
-   }
+      file->flags |= (O_CREAT | O_TRUNC);
+
+   // Construct the "base part."
+   output_t* output = make_output(settings);
+
+   // Select the open, consume bytes, and close functions based on file type.
    output->consume_bytes = write_bytes_to_image;
-   if (STREQ(output->current_file->name, "stdout"))
-   {
+   if (STREQ(file->name, "stdout")) {
       output->open = connect_to_std_out;
       output->close = disconnect_from_std_out;
-   }
-   else
-   {
+   } else {
       output->open = open_file_output;
       output->close = close_file_output;
    }
-   output->expected_hashes = expected_hashes;
+
+   // Construct the "derived part."
+   output->file = (file_output_t*)malloc(sizeof(file_output_t));
+   output->file->current_file = file;
+   output->file->files = file->part_of_set ? file : NULL;
+   output->file->max_file_size = settings->max_output_file_size;
+   output->file->sectors_to_skip = settings->output_sectors_to_skip;
+   output->file->bytes_output = 0;
+   output->file->verification = file->verification;
+   output->file->append_garbage_bytes = settings->corrupt_output;
+   output->file->expected_hashes = expected_hashes;
+   output->file->actual_hashes = NULL;
+
    return output;
 }
 
 static output_t*
-make_hash_output(settings_t* settings, hash_algorithm_t* algorithm)
+make_hash_output(settings_t *settings, hash_algorithm_t *algorithm, enum VERIFICATION_TYPE verification)
 {
-   hash_t* hash = make_hash(algorithm);
-   if (settings->splitting_output && settings->verifying_output)
-   {
-      hash->current_piece = make_hash(algorithm);
-      hash->pieces = hash->current_piece; 
-      hash->piecewise_hash_length = settings->max_output_file_size;
-   }
-
-   output_t* output =  make_output(settings);
-   output->hash = hash;
+   output_t* output = make_output(settings);
+   output->hash = (hash_output_t*)malloc(sizeof(hash_output_t));
+   output->hash->algorithm = algorithm;
+   output->hash->total_hash = make_hash(algorithm);
+   output->hash->total_hash_length = INFINITE_BYTES;
+   output->hash->current_piece = NULL;
+   output->hash->piecewise_hash = NULL;
+   output->hash->piecewise_hash_length = 0; 
+   output->hash->device_hash = verification == DEVICE_FULL ? make_hash(algorithm) : NULL;
+   output->hash->next = NULL;
    output->open = open_hash; 
-   output->consume_bytes = hash_bytes;
+   output->consume_bytes = verification == DEVICE_FULL ? hash_device_bytes : hash_bytes;
    output->close = close_hash;
+
+   if (settings->splitting_output && settings->verifying_output) {
+      output->hash->current_piece = make_hash(algorithm);
+      output->hash->piecewise_hash = output->hash->current_piece; 
+      output->hash->piecewise_hash_length = settings->max_output_file_size;
+   }
+  
    return output;
 }
 
 static output_t*
-make_hash_outputs(settings_t* settings)
+make_hash_outputs(settings_t *settings, enum VERIFICATION_TYPE verification)
 {
-   output_t* hash_outputs = NULL;
+   output_t *hash_outputs = NULL;
 
-   for (uint8_t i = 0 ; i < NUM_HASHES ; ++i)
-   {
-      if (hash_algorithms[i].active)
-      {
-         add_to_output_list(&hash_outputs, make_hash_output(settings, &hash_algorithms[i]));
+   for (uint8_t i = 0 ; i < NUM_HASHES ; ++i) {
+      if (hash_algorithms[i].active) {
+         add_to_output_list(&hash_outputs, make_hash_output(settings, &hash_algorithms[i], verification));
       }
    }
    
    return hash_outputs;
 }
 
+static void
+make_hash_outputs_list(output_t *outputs, hash_output_t **hash_outputs)
+{
+   for (output_t *output = outputs; output; output = output->next_output) {
+      if (output->hash)
+         add_to_hash_output_list(hash_outputs, output->hash);
+   }
+}
+
 static input_t*
 make_input(settings_t* settings)
 {
@@ -2997,6 +2938,7 @@ make_input(settings_t* settings)
    input->state =  PENDING;
    input->sector_size = settings->sector_size;
    input->max_sectors_to_input = settings->max_sectors_to_input; 
+   input->bytes_to_input = 0;
    input->bytes_input = 0;   
    input->current_file = NULL;
    input->files = NULL;
@@ -3021,48 +2963,36 @@ make_input(settings_t* settings)
    input->buffer.data = (char*)ptr_align(input->buffer.data, getpagesize());
    input->buffer.length = 0;
   
-   if (settings->input_file)
-   {
+   // Select the open, produce_bytes, and close functions based on input type.
+   if (settings->input_file) {
       input->current_file = settings->input_file;
       if (input->current_file->part_of_set)
-      {
          input->files = input->current_file;
-      }
 
-      if (STREQ(input->current_file->name, "stdin"))
-      {
+      if (STREQ(input->current_file->name, "stdin")) {
 	 input->open = connect_to_std_in;
 	 input->produce_bytes = read_bytes_from_file;
 	 input->close = disconnect_from_std_in;
       }
-      else
-      {
+      else {
          input->open = open_file_input;
 
          // This ASSUMES that probe_file() was called on settings->input_file
          // before calling this function.
-	 if (input->current_file->is_device)
-	 {
+	 if (input->current_file->is_device) {
 	    if (input->recover_errors)
-	    {
 	       input->current_file->flags |= O_DIRECT;
-	    }
 	    input->produce_bytes = read_bytes_from_device;
 	 }
 	 else if (input->current_file->part_of_set)
-	 {
 	    input->produce_bytes = read_bytes_from_files;
-	 }
 	 else
-	 { 
 	    input->produce_bytes = read_bytes_from_file;
-	 }
 
          input->close = close_file_input;
       }
    }
-   else
-   {
+   else {
       input->pattern_string = strdup(settings->input_pattern_string);
       input->pattern = strdup(settings->input_pattern);
       input->pattern_length = settings->input_pattern_length;
@@ -3082,30 +3012,28 @@ add_verification_job(job_t* job, settings_t* settings)
 
    // Make a verification task for each output to be verified.
    task_t* verification_tasks = NULL;
-   output_t* output = imaging_task->outputs;
-   while (output)
-   {
-      if (output->current_file && output->current_file->verify_requested)
-      {
+   for (output_t* output = imaging_task->outputs; output; output = output->next_output) {
+      if (output->file && output->file->verification != NONE) {
          // Make an input file corresponding to the output file
          // and swap it into the already initialized and validated settings.
-         // However, make sure to clear max sectors to input, since the size of the
-         // verification target is now the determinant of bytes to input.
-         settings->input_file = make_file(output->current_file->unparsed_name,
-            0, O_RDONLY, output->current_file->part_of_set, false);
-         settings->max_sectors_to_input = INFINITE_BYTES;
+         settings->input_file = make_file(output->file->current_file->unparsed_name,
+            0, O_RDONLY, output->file->current_file->part_of_set, output->file->verification);
 	 input_t* input = make_input(settings);   
 
+         // If output sectors were skipped, those sectors need to be skipped
+         // for verification, too.
+         if (output->file->sectors_to_skip > 0)
+            input->sectors_to_skip = output->file->sectors_to_skip;
+
          // Prepare the output hashes and cache a pointer to the list
          // for later comparision with the list of input hashes cached
          // in the output when the imaging job was created. 
-	 output_t* output_hashes = make_hash_outputs(settings);
-         output->actual_hashes = output_hashes;
+         output_t *output_hashes = make_hash_outputs(settings, output->file->verification);
+         make_hash_outputs_list(output_hashes, &output->file->actual_hashes);    
 
-         task_t* verification_task = make_task(input, output_hashes);
+         task_t* verification_task = make_task(input, output_hashes, output->file);
 	 add_to_task_list(&verification_tasks, verification_task);
       }
-      output = output->next_output;
    }
  
    job->next_job = make_job(verification_tasks, report_verification_progress);
@@ -3115,36 +3043,36 @@ static job_t*
 make_imaging_job(settings_t* settings)
 {
    input_t* input = make_input(settings);   
-   output_t* input_hashes = make_hash_outputs(settings);
+
+   output_t* input_hashes = make_hash_outputs(settings, NONE);
+   hash_output_t* expected_hashes = NULL;
+   make_hash_outputs_list(input_hashes, &expected_hashes);
 
    // Make the file outputs.
    output_t* outputs = NULL;
    if (settings->wipe_target)
    {
-      add_to_output_list(&outputs, make_file_output(settings, settings->wipe_target, input_hashes));
+      add_to_output_list(&outputs, make_file_output(settings, settings->wipe_target, expected_hashes));
    } 
    else
    {
       file_t* file = settings->output_files;
       while (file)
       {
-	 output_t* output = make_file_output(settings, file, input_hashes);
+	 output_t* output = make_file_output(settings, file, expected_hashes);
 	 add_to_output_list(&outputs, output);
 	 file = file->next_file;
 	 
-	 // Cache a pointer to the input hashes for verification purposes.
-	 output->expected_hashes = input_hashes;
-
 	 // Unlink the file for tidiness, and so that the next_file pointer
 	 // can be used to make a list of files (if splitting the output). 
-	 output->current_file->next_file = NULL;
+	 output->file->current_file->next_file = NULL;
       }
    }
 
    // Append the hash outputs for the input hashes to the file outputs. 
    add_to_output_list(&outputs, input_hashes);
 
-   task_t* task = make_task(input, outputs);
+   task_t* task = make_task(input, outputs, NULL);
    return make_job(task, report_imaging_progress);
 }
 
@@ -3165,12 +3093,12 @@ make_jobs(settings_t* settings)
 static void 
 check_device_for_hpa_dco(file_t* device)
 {
-   if (device->is_device)
+   if (device->type == DEVICE)
    {
       device->descriptor = open(device->name, O_RDONLY, device->perms); 
       if (device->descriptor >= 0)
       {   
-         report_to_log(_("checking for HPA/DCO: "));
+         report(_("checking for HPA/DCO: "), JOB_LOGS);
          
          int err = 0;
          __u16 *id = (void *)-1;
@@ -3197,7 +3125,7 @@ check_device_for_hpa_dco(file_t* device)
 
          if (!dci || !id || !native)
          {
-            report_to_log(_("device doesn't support ATA commands\n"));   
+            report(_("device doesn't support ATA commands\n"), JOB_LOGS);   
             return;
          }
 
@@ -3218,19 +3146,19 @@ check_device_for_hpa_dco(file_t* device)
 
             if (hpa && dco)
             {
-               report_to_log(_("HPA and DCO found\n"));   
+               report(_("HPA and DCO found\n"), JOB_LOGS);   
             }
             else if (hpa)
             {
-               report_to_log(_("HPA found\n"));   
+               report(_("HPA found\n"), JOB_LOGS);   
             }
             else if (dco)
             {
-               report_to_log(_("DCO found\n"));   
+               report(_("DCO found\n"), JOB_LOGS);   
             }
             else 
             {
-               report_to_log(_("none\n"));   
+               report(_("none\n"), JOB_LOGS);   
             }
 
             char limits[DISPLAY_MESSAGE_LENGTH];
@@ -3238,17 +3166,17 @@ check_device_for_hpa_dco(file_t* device)
             if (hpa)
             {
                sprintf(limits, _("HPA limit: %11llu sectors\n"));
-               report_to_log(limits, true);         
+               report(limits, JOB_LOGS);         
             }
 
             if (dco)
             {
                sprintf(limits, _("DCO limit: %11llu sectors\n"));
-               report_to_log(limits);         
+               report(limits, JOB_LOGS);         
             }
 
             sprintf(limits, _("full size: %11llu sectors\n"), maximum_lba);
-            report_to_log(limits);         
+            report(limits), JOB_LOGS;         
          }
 
          if (close(device->descriptor) == 0)
@@ -3271,7 +3199,7 @@ report_device_size(file_t* device)
 {
    char stats[DISPLAY_MESSAGE_LENGTH];
    sprintf(stats, _("device size: %"PRIuMAX" sectors (probed)\n"), device->probed_size_in_sectors);
-   report_to_log(stats);   
+   report(stats, JOB_LOGS);   
    
    #if USE_HDPARM
    #ifdef __linux__
@@ -3296,20 +3224,16 @@ report_device_size(file_t* device)
 }
 
 static void
-report_input_size(settings_t* settings)
+report_input_size(settings_t *settings)
 {
    if (settings->wipe_target)
-   {
       report_device_size(settings->wipe_target); 
-   }
    else if (settings->input_file && settings->input_file->is_device)
-   {
       report_device_size(settings->input_file);
-   }
    
    char message[DISPLAY_MESSAGE_LENGTH];
    sprintf(message, "sector size: %zd bytes (%s)\n", settings->sector_size, settings->sector_size_source);
-   report_to_log(message);
+   report(message, JOB_LOGS);
    flush_logs();
 }
 
@@ -3387,79 +3311,64 @@ probe_file_for_validation(file_t* file)
 }
 
 static void
-add_wipe_target(settings_t* settings, const char* device_name, bool verify_requested)
+add_wipe_target(settings_t* settings, const char* device_name, enum VERIFICATION_TYPE verification)
 {
    // Note the early outs for command line errors since evidence should not
    // be handled any more than necessary, and glossing over a malformed
    // command line is therefore undesirable - the user's choices need to be
    // exactly specified before a run is undertaken.
+
    if (settings->wipe_target)
-   {
-      report_error(DC3DD_EXIT_ABORTED, 0, _("cannot specify wipe= or vwipe= more than once"));      
-   }   
+      report_error(DC3DD_EXIT_ABORTED, 0, _("cannot specify wipe= or hwipe= more than once"));      
 
    if (settings->output_files)
-   {
-      report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and of=, hof=, ofs= or hofs="));
-   }
+      report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and of=, hof=, ofs= or hofs="));
   
-   settings->wipe_target = make_file(device_name, 0, O_WRONLY, false, verify_requested);
+   settings->wipe_target = make_file(device_name, 0, O_WRONLY, false, verification);
    DC3DD_ASSERT(settings->wipe_target != NULL);
  
    // Make sure the wipe target is valid.
    probe_file_for_validation(settings->wipe_target);
    if (!settings->wipe_target->is_device)
-   {
       report_error(DC3DD_EXIT_ABORTED, errno, _("%s not recognized as a device, cannot wipe"),
 	quote(settings->wipe_target->name));
-   }
    if (settings->wipe_target->probed_size_in_bytes <= 0)
-   {
       report_error(DC3DD_EXIT_ABORTED, errno, _("%s size probe failed, cannot wipe"),
          quote(settings->wipe_target->name));
-   }
 
-   if (verify_requested)
-   {
+   if (verification != NONE)
       settings->verifying_output = true;
-   }
 }
 
 static void 
-add_output_file(settings_t* settings, const char* file_name, bool part_of_set, bool verify_requested)
+add_output_file(settings_t *settings, const char *file_name, bool part_of_set,
+   enum VERIFICATION_TYPE verification)
 {
-   // Note the early outs for command line errors since evidence should not
-   // be handled any more than necessary, and glossing over a malformed
-   // command line is therefore undesirable - the user's choices need to be
-   // exactly specified before a run is undertaken.
-
-   if (verify_requested)
-   {
+   if (verification != NONE) {
       if (STREQ(file_name, "/dev/null"))
-      {
-         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot specify hof=/dev/null"));
-      }
+         report_error(DC3DD_EXIT_ABORTED, 0, 
+         _("cannot output to /dev/null if using hof=, hofs=, phod=, or fhod="));
       else
-      {
          settings->verifying_output = true;
-      }
    }
 
    if (part_of_set)
-   {
       settings->splitting_output = true;
-   }
 
-   file_t* file = make_file(file_name, 0, O_WRONLY, part_of_set, verify_requested);
-   if (file)
-   {
+   file_t* file = make_file(file_name, 0, O_WRONLY, part_of_set, verification);
+   if (file) {
+      if (verification == DEVICE_PARTIAL || verification == DEVICE_FULL) {
+         probe_file_for_validation(file);
+         if (!file->is_device)
+            report_error(DC3DD_EXIT_ABORTED, errno, 
+               _("%s not recognized as a device, cannot specify %s"),
+	      quote(file->name), verification == DEVICE_PARTIAL ? "phod=" : "fhod=");
+      }
       add_to_file_list(&settings->output_files, file);
    }
    else
-   {
       report_error(DC3DD_EXIT_ABORTED, 0, _("%s not valid BASE.FMT specifier for %s"),
-         quote(file_name), verify_requested ? "hofs=" : "ofs=");
-   }
+         quote(file_name), verification != NONE ? "hofs=" : "ofs=");
 }
 
 static void
@@ -3471,9 +3380,7 @@ add_input_text_pattern(settings_t* settings, const char* pattern)
    // exactly specified before a run is undertaken.
 
    if (settings->input_file || settings->input_pattern)
-   {
       report_error(DC3DD_EXIT_ABORTED, 0, _("use only one of pat=, tpat=, if=, ifs="));
-   }
 
    settings->input_pattern_string = strdup(pattern); 
    settings->input_pattern = strdup(pattern); 
@@ -3552,34 +3459,22 @@ add_input_pattern(settings_t* settings, const char* pattern)
 }
 
 static void
-add_input_file(settings_t* settings, const char* file_name, bool part_of_set)
+add_input_file(settings_t *settings, const char *file_name, bool part_of_set)
 {
-   // Note the early outs for command line errors since evidence should not
-   // be handled any more than necessary, and glossing over a malformed
-   // command line is therefore undesirable - the user's choices need to be
-   // exactly specified before a run is undertaken.
    
    if (settings->input_file || settings->input_pattern)
-   {
       report_error(DC3DD_EXIT_ABORTED, 0, _("use only one of pat=, tpat=, if=, ifs="));
-   }
 
-   if (STREQ(file_name, "/dev/zero"))
-   {
+   if (STREQ(file_name, "/dev/zero")) 
       add_input_pattern(settings, "00");
-   }
    else
    {
-      settings->input_file = make_file(file_name, 0, O_RDONLY, part_of_set, false);
+      settings->input_file = make_file(file_name, 0, O_RDONLY, part_of_set, NONE);
       if (!settings->input_file)
-      {
 	 report_error(DC3DD_EXIT_ABORTED, 0 , _("%s not valid BASE.FMT form for ifs="), file_name);
-      }
 
       if (!STREQ(file_name, "stdin"))
-      {
 	 probe_file_for_validation(settings->input_file);
-      }
    }
 }
 
@@ -3698,7 +3593,7 @@ validate_hashing_settings(settings_t* settings)
    if (settings->verifying_output && !hash_specified)
    {
       report_error(DC3DD_EXIT_ABORTED, 0,
-         _("hof=, hofs=, or vwipe= specified without hash algorithm(s) selection"));
+         _("hof=, hofs=, or hwipe= specified without hash algorithm(s) selection"));
    }
 }   
 
@@ -3720,7 +3615,9 @@ validate_size_settings(settings_t* settings)
    {
       settings->sector_size_source = _("set");
    }
-   else if (settings->input_file && settings->input_file->is_device && settings->input_file->probed_sector_size > 0)
+   else if (settings->input_file && 
+            settings->input_file->is_device && 
+            settings->input_file->probed_sector_size > 0)
    {
       settings->sector_size = settings->input_file->probed_sector_size;
       settings->sector_size_source = _("probed");
@@ -3748,6 +3645,7 @@ validate_size_settings(settings_t* settings)
          _("if iskip= is specified, if= must specify an input file or device of at least that size"));
    }
 
+
    if (settings->output_sectors_to_skip)
    {
       file_t* file = settings->output_files;
@@ -3827,7 +3725,7 @@ validate_IO_options(settings_t* settings)
    {
       if (settings->max_output_file_size != INFINITE_BYTES)
       {  
-         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and ofsz="));                
+         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and ofsz="));                
       }    
 
       if (settings->max_sectors_to_input == INFINITE_SECTORS)
@@ -3838,27 +3736,27 @@ validate_IO_options(settings_t* settings)
       }
       else
       {    
-         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and cnt="));                
+         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and cnt="));                
       }    
 
       if (settings->input_sectors_to_skip)
       {    
-         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and iskip="));                
+         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and iskip="));                
       }    
 
       if (settings->output_sectors_to_skip)
       {    
-         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and oskip="));                
+         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and oskip="));                
       }    
 
       if (settings->append_output)
       {    
-         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and app=on"));                
+         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and app=on"));                
       }    
     
       if (settings->sector_size)
       {    
-         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and ssz="));                
+         report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and ssz="));                
       }
    }
 
@@ -3903,12 +3801,12 @@ validate_IO_combination(settings_t* settings)
    
    if (settings->wipe_target && settings->output_files)
    {
-      report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and of=, hof=, ofs= or hofs="));   
+      report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and of=, hof=, ofs= or hofs="));   
    }
 
    if (settings->wipe_target && settings->input_file)
    {
-      report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or vwipe= and if= or ifs="));      
+      report_error(DC3DD_EXIT_ABORTED, 0, _("cannot combine wipe= or hwipe= and if= or ifs="));      
    }
 
    if (settings->wipe_target && !settings->input_pattern)
@@ -3926,7 +3824,7 @@ validate_IO_combination(settings_t* settings)
    if (!settings->wipe_target && !settings->output_files)
    {
       // No outputs specified, default to writing to stdout.
-      add_output_file(settings, "stdout", false, false);
+      add_output_file(settings, "stdout", false, NONE);
    }
 
    DC3DD_ASSERT((settings->wipe_target && settings->input_pattern) || 
@@ -3972,105 +3870,83 @@ make_settings()
 } 
 
 static settings_t*
-parse_args(int argc, char* const* argv)
-{
+parse_args(int argc, char *const *argv) {
    settings_t* settings = make_settings();
 
-   for (int i = optind; i < argc; ++i)
-   {
+   // Note that parsing and validation of the command line is very strict
+   // since evidence should not be handled any more than necessary. It is
+   // therefore best if the user's choices are exactly specified before a
+   // run is performed.
+   for (int i = optind; i < argc; ++i) {
       // Split the argument into a name/value pair.
-      char const* name = argv[i];
-      char const* val = strchr(name, '=');
-      if (!val)
-      {
-	 // Note the early out for command line errors since evidence should not
-	 // be handled any more than necessary, and glossing over a malformed
-	 // command line is therefore undesirable - the user's choices need to be
-	 // exactly specified before a run is undertaken.
+      char const *name = argv[i];
+      char const *val = strchr(name, '=');
+      if (!val) {
          report_error(DC3DD_EXIT_ABORTED, 0, _("unrecognized option %s"), quote(name));
       }
       ++val;
 
-      if (option_is(name, "if"))
-      {
+      if (option_is(name, "if")) {
          add_input_file(settings, val, false);
       }
-      else if (option_is(name, "ifs"))
-      {
+      else if (option_is(name, "ifs")) {
          add_input_file(settings, val, true);         
       }
-      else if (option_is(name,"pat"))
-      {
+      else if (option_is(name,"pat")) {
          add_input_pattern(settings, val);
       }
-      else if (option_is(name,"tpat"))
-      {
+      else if (option_is(name,"tpat")) {
          add_input_text_pattern(settings, val);
       }
-      else if (option_is(name, "of"))
-      {
-         add_output_file(settings, val, false, false);
+      else if (option_is(name, "of")) {
+         add_output_file(settings, val, false, NONE);
       }
-      else if (option_is(name, "hof"))
-      {
-         add_output_file(settings, val, false, true);
+      else if (option_is(name, "hof")) {
+         add_output_file(settings, val, false, STANDARD);
       }
-      else if (option_is(name, "ofs"))
-      {
-         add_output_file(settings, val, true, false);
+      else if (option_is(name, "phod")) {
+         add_output_file(settings, val, false, DEVICE_PARTIAL);
+      }
+      else if (option_is(name, "fhod")) {
+         add_output_file(settings, val, false, DEVICE_FULL);
+      }
+      else if (option_is(name, "ofs")) {
+         add_output_file(settings, val, true, NONE);
       }  
-      else if (option_is(name, "hofs"))
-      {
-         add_output_file(settings, val, true, true);
+      else if (option_is(name, "hofs")) {
+         add_output_file(settings, val, true, STANDARD);
       }  
-      else if (option_is(name,"hash"))
-      {
+      else if (option_is(name,"hash")) {
          activate_hash(val);      
       }
-      else if (option_is(name,"log") || option_is(name, "hlog"))
-      {
+      else if (option_is(name,"log") || option_is(name, "hlog")) {
          // Arg already parsed in initiate_logging(). 
       }
-      else if (option_is(name,"rec"))
-      {
-         // i.e., rec=off
+      else if (option_is(name,"rec")) {
          settings->recover_errors = false;
       }
-      else if (option_is(name, "app"))
-      {
-         // i.e., app=on
+      else if (option_is(name, "app")) {
          settings->append_output = true;
       }
-      else if (option_is(name, "wipe")) 
-      {
+      else if (option_is(name, "wipe")) {
          add_wipe_target(settings, val, false); 
       }
-      else if (option_is(name, "vwipe")) 
-      {
+      else if (option_is(name, "hwipe")) {
          add_wipe_target(settings, val, true); 
       }
-      else if (option_is(name, "verb"))
-      {
-         // i.e., verb=on
+      else if (option_is(name, "verb")) {
          verbose_reporting = true;
       }
-      else if (option_is(name, "nwspc"))
-      {
-         // i.e., nwspc=on
+      else if (option_is(name, "nwspc")) {
          compact_reporting = true;
       }
-      else if (option_is(name, "b10"))
-      {
-         // i.e., dbr=on
+      else if (option_is(name, "b10")) {
          progress_bytes_reporting_flag = 0;
       }
-      else if (option_is(name, "corruptoutput"))
-      {
-         // i.e., corruptoutput=on
+      else if (option_is(name, "corruptoutput")) {
          settings->corrupt_output = true;
       }
-      else
-      {
+      else {
          parse_quantifier(settings, name, val);
       }
    }    
@@ -4169,39 +4045,36 @@ report_compile_flags(FILE* file, bool newlines)
 }
 
 static void
-report_settings(int argc, char* const* argv)
+report_command_line(int argc, char* const* argv)
 {
    // Report compiled-in options.
    fputs(_("compiled options:"), stderr);
    report_compile_flags(stderr, false);
-   if (log != NULL)
-   {
-      fputs(_("compiled options:"), log);
-      report_compile_flags(log, false);
+   for (log_t* log = job_logs; log; log = log->next_log) {
+      fputs(_("compiled options:"), log->file);
+      report_compile_flags(log->file, false);
    }
-   if (hash_log != NULL)
-   {
-      fputs(_("compiled options:"), hash_log);
-      report_compile_flags(hash_log, false);
+   for (log_t* log = hash_logs; log; log = log->next_log) {
+      fputs(_("compiled options:"), log->file);
+      report_compile_flags(log->file, false);
    }
 
    // Report the command line.
    char* command_line = make_cmd_line_string(argc, argv);
    char message[DISPLAY_MESSAGE_LENGTH];
    sprintf(message, _("command line: %s\n"), command_line);
-   report_to_all(message);
+   report(message, ALL_LOGS);
    free(command_line);
    flush_logs();
 }
  
 static void
-report_start_message()
+report_startup_message()
 {
-   // Save the program start time in a global for later use in 
-   // progress messages.
+   // Save the program start time for later use in progress messages.
    start_time = gethrxtime();
 
-   // Write the start message to all logs (i.e., console, log, hash log).
+   // Write the start message to all job_logs (i.e., console, log, hash log).
    // The message acts as a sort of header for the run. The leading newline
    // character acts to separate the output from multiple runs when
    // appending to an existing log.
@@ -4209,69 +4082,64 @@ report_start_message()
    char message[DISPLAY_MESSAGE_LENGTH];
    sprintf(message, "\n%s %s started at %s\n", PROGRAM_NAME, VERSION, formatted_start_time);
    free(formatted_start_time);
-   report_to_all(message);
+   report(message, ALL_LOGS);
    flush_logs();
 }
 
 static void
-open_log(const char* arg, const char* arg_name, FILE** log)
+open_log(const char *arg, const char *arg_name, log_t **logs)
 {
-   // Check for duplication of a log argument in the command line.
-   // Note the early out for command line errors since evidence should not
-   // be handled any more than necessary, and glossing over a malformed
-   // command line is therefore undesirable - the user's choices need to be
-   // exactly specified before a run is undertaken.
-   char message[DISPLAY_MESSAGE_LENGTH];
-   if (*log != NULL)
-   {
-      sprintf(message, _("cannot specify %s more than once"), arg_name);
-      report_error(DC3DD_EXIT_ABORTED, 0, message);
-   }
-
    // Extract the log file name from the command line argument.
-   const char* val = strchr(arg, '=');
-   if (!val)
-   {
+   const char *val = strchr(arg, '=');
+   if (!val) {
+      char message[DISPLAY_MESSAGE_LENGTH];
       sprintf(message, _("%s specified with no file name"), arg_name);
       report_error(DC3DD_EXIT_ABORTED, 0, message);
    }
    ++val;
 
-   // Open the log in append mode to support use cases where the
+   // Open the log file in append mode to support use cases where the
    // imaging is performed using multiple runs (e.g., using skips
    // etc., to work around errors) and a "cumulative" record of
    // the runs is desired.
-   *log = fopen(val, "a");
-   if (*log == NULL)
-   {
+   FILE *file = fopen(val, "a");
+   if (!file)
       report_error(DC3DD_EXIT_ABORTED, errno, _("opening log %s"), quote(val));
-   }
+
+   // Add the log to the logs list specified by the caller.
+   log_t* log = (log_t*)malloc(sizeof(log_t));
+   log->file = file;
+   log->next_log = NULL;
+   add_to_log_list(logs, log);
+
+   // Add the log to the master logs list.
+   log = (log_t*)malloc(sizeof(log_t));
+   log->file = file;
+   log->next_log = NULL;
+   add_to_log_list(&all_logs, log);
 }
 
 static void 
 initiate_logging(int argc, char* const* argv)
 {
-   // Use PTHREAD_MUTEX_RECURSIVE to initialize the global mutex for
-   // synchronizing reporting output to the log(s) and console, so that nested
-   // calls to functions like report_to_log() that lock the mutex are safe.   
+   // Initialize a mutex for synchronizing output to the log(s) and console.
+   // Use PTHREAD_MUTEX_RECURSIVE so that nested calls to functions that lock
+   // the mutex are safe.   
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&reporting_lock, &attr);
    pthread_mutexattr_destroy(&attr);
 
-   // Look for command line options specifying logs.
+   // Look for command line options specifying job_logs.
    const char* arg = NULL;
-   for (int i = optind; i < argc; ++i)
-   {
+   for (int i = optind; i < argc; ++i) {
       arg = argv[i];
-      if (option_is(arg, "log"))
-      {
-         open_log(arg, "log=", &log);
+      if (option_is(arg, "log")) {
+         open_log(arg, "log=", &job_logs);
       }
-      else if (option_is(arg, "hlog"))
-      {
-         open_log(arg, "hlog=", &hash_log);
+      else if (option_is(arg, "hlog")) {
+         open_log(arg, "hlog=", &hash_logs);
       }
    }
 }
@@ -4279,64 +4147,65 @@ initiate_logging(int argc, char* const* argv)
 void
 usage(int status)
 {
-   if (status != EXIT_SUCCESS)
-   {
+   if (status != EXIT_SUCCESS) {
       fprintf (stderr, _("Try `%s --help' for more information.\n"), program_name);
    }
-   else
-   {
+   else {
       fputs(_("------\n"), stderr);
       fputs(_("usage:\n"), stderr);
       fputs(_("------\n\n"), stderr);
       fprintf(stderr, _("\t%s [OPTION 1] [OPTION 2] ... [OPTION N]\n"), program_name);    
       fputs("\n", stderr);
-      fprintf(stderr, _("\t\t*or*\n"), program_name);    
+      fprintf(stderr, _("\t\t*or*\n"));    
       fputs("\n", stderr);
       fprintf(stderr, _("\t%s [HELP OPTION]\n"), program_name);    
       fputs("\n", stderr);
-      fprintf(stderr, _("\twhere each OPTION is selected from the basic or advanced\n"), program_name);    
-      fprintf(stderr, _("\toptions listed below, or HELP OPTION is selected from the\n"), program_name);    
-      fprintf(stderr, _("\thelp options listed below.\n\n"), program_name);    
+      fprintf(stderr, _("\twhere each OPTION is selected from the basic or advanced\n"));    
+      fprintf(stderr, _("\toptions listed below, or HELP OPTION is selected from the\n"));    
+      fprintf(stderr, _("\thelp options listed below.\n\n"));    
 
       fputs(_("--------------\n"), stderr);
       fputs(_("basic options:\n"), stderr);
       fputs(_("--------------\n\n"), stderr);
-      fprintf(stderr, "\t%-21s%s\n", _("if=FILE"), _("Read input from the device or regular file FILE"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("(see note #1 below). This option can only be used"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("once and cannot be combined with ifs=, pat=,"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("or tpat=."));
-      if (!O_DIRECT)
-      {
+      fprintf(stderr, "\t%-21s%s\n", _("if=DEVICE or FILE"), _("Read input from a device or a file (see note #1"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("below for how to read from standard input). This"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("option can only be used once and cannot be"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("combined with ifs=, pat=, or tpat=."));
+      if (!O_DIRECT) {
          fprintf(stderr, "\t%-21s%s\n", "", _("If FILE is a device, use rdisk for"));
          fprintf(stderr, "\t%-21s%s\n", "", _("direct (unbuffered) input to enable read error"));
          fprintf(stderr, "\t%-21s%s\n", "", _("recovery unless rec=off is specified."));
       }
       fprintf(stderr, "\t%-21s%s\n", _("ifs=BASE.FMT"), _("Read input from a set of files with base name"));
       fprintf(stderr, "\t%-21s%s\n", "", _("BASE and sequential file name extensions"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("conforming to the format specifier FMT (see"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("note #4 below). This option can only be used once"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("and cannot be combined with if=, pat=, or"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("tpat=."));
-      fprintf(stderr, "\t%-21s%s\n", _("of=FILE"), _("Write output to FILE (see note #2 below). This"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("conforming to the format specifier FMT (see note"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("#4 below for how to specify FMT). This option"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("can only be used once and cannot be combined with"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("if=, pat=, or tpat=."));
+      fprintf(stderr, "\t%-21s%s\n", _("of=FILE or DEVICE"), _("Write output to a file or device (see note #2"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("below for how to write to standard output). This")); 
       fprintf(stderr, "\t%-21s%s\n", "", _("option can be used more than once (see note #3")); 
-      fprintf(stderr, "\t%-21s%s\n", "", _("below).")); 
-      fprintf(stderr, "\t%-21s%s\n", _("hof=FILE"), _("Write output to FILE and verify FILE after writing"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("it by hashing it and comparing the output hash(es)"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("to the input hash(es). This option can be used more"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("than once (see note #3 below)."));
+      fprintf(stderr, "\t%-21s%s\n", "", _("below for how to generate multiple outputs).")); 
+      fprintf(stderr, "\t%-21s%s\n", _("hof=FILE or DEVICE"), _("Write output to a file or device, hash the"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("output file or device, and verify by comparing"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("the output hash(es) to the input hash(es). This"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("option can be used more than once (see note #3"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("below for how to generate multiple outputs)."));
       fprintf(stderr, "\t%-21s%s\n", _("ofs=BASE.FMT"), _("Write output to a set of files with base name BASE"));
       fprintf(stderr, "\t%-21s%s\n", "", _("and sequential file name extensions generated from")); 
-      fprintf(stderr, "\t%-21s%s\n", "", _("the format specifier FMT (see note #4 below). This"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("option can be used more than once (see note #3"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("below). Specify the maximum size of each file"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("in the set using ofsz=."));
+      fprintf(stderr, "\t%-21s%s\n", "", _("the format specifier FMT (see note #4 below for"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("how to specify FMT). This option can be used more"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("than once (see note #3 below for how to generate"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("multiple outputs). Specify the maximum size of"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("each file in the set using ofsz=."));
       fprintf(stderr, "\t%-21s%s\n", _("hofs=BASE.FMT"), _("Write output to a set of files with base name BASE"));
       fprintf(stderr, "\t%-21s%s\n", "", _("and sequential file name extensions generated from"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("the format specifier FMT (see note #4 below)."));
-      fprintf(stderr, "\t%-21s%s\n", "", _("Verify the files after writing them by hashing"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("them and comparing the output hash(es) to the input"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("hash(es). This option can be used more than once"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("(see note #3 below). Specify the maximum size of"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("the format specifier FMT (see note #4 below for"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("how to specify FMT). Hash the output files and"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("verify by comparing the output hash(es) to the"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("input hash(es). This option can be used more than"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("once (see note #3 below for how to generate"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("multiple outputs). Specify the maximum size of"));
       fprintf(stderr, "\t%-21s%s\n", "", _("each file in the set using ofsz=."));
       fprintf(stderr, "\t%-21s%s\n", _("ofsz=BYTES"), _("Set the maximum size of each file in the sets of"));
       fprintf(stderr, "\t%-21s%s\n", "", _("files specified using ofs= or hofs= to"));
@@ -4345,32 +4214,44 @@ usage(int status)
       fprintf(stderr, "\t%-21s%s\n", "", _("-DDEFAULT_OUTPUT_FILE_SIZE followed by the desired"));
       fprintf(stderr, "\t%-21s%s\n", "", _("value in BYTES.")); 
       fprintf(stderr, "\t%-21s%s\n", _("hash=ALGORITHM"), _("Compute an ALGORITHM hash of the input and also"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("of any outputs specified using hof= or hofs=,"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("where ALGORITHM is one of md5, sha1, sha256, or"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("sha512. This option may be used once for each"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("supported ALGORITHM. Alternatively, hashing can"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("be activated at compile time using one or more of"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("-DDEFAULT_HASH_MD5,-DDEFAULT_HASH_SHA1,"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("of any outputs specified using hof=, hofs=, phod=,"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("or fhod=, where ALGORITHM is one of md5, sha1,"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("sha256, or sha512. This option may be used once"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("for each supported ALGORITHM. Alternatively,"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("hashing can be activated at compile time using one"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("or more of -DDEFAULT_HASH_MD5,-DDEFAULT_HASH_SHA1,"));
       fprintf(stderr, "\t%-21s%s\n", "", _("-DDEFAULT_HASH_SHA256, and -DDEFAULT_HASH_SHA512.")); 
       fprintf(stderr, "\t%-21s%s\n", _("log=FILE"), _("Log I/O statistcs, diagnostics, and total hashes"));
       fprintf(stderr, "\t%-21s%s\n", "", _("of input and output to FILE. If hlog= is not"));
       fprintf(stderr, "\t%-21s%s\n", "", _("specified, piecewise hashes of multiple file"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("input and output are also logged to FILE."));
-      fprintf(stderr, "\t%-21s%s\n\n", _("hlog=FILE"), _("Log total hashes and piecewise hashes to FILE."));
+      fprintf(stderr, "\t%-21s%s\n", "", _("input and output are also logged to FILE. This"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("option can be used more than once to generate")); 
+      fprintf(stderr, "\t%-21s%s\n", "", _("multiple logs.")); 
+      fprintf(stderr, "\t%-21s%s\n", _("hlog=FILE"), _("Log total hashes and piecewise hashes to FILE."));
+      fprintf(stderr, "\t%-21s%s\n", "", _("This option can be used more than once to generate")); 
+      fprintf(stderr, "\t%-21s%s\n\n", "", _("multiple logs.")); 
 
       fputs(_("-----------------\n"), stderr);
       fputs(_("advanced options:\n"), stderr);
       fputs(_("-----------------\n\n"), stderr);
+      fprintf(stderr, "\t%-21s%s\n", _("phod=DEVICE"), _("The same as hof=DEVICE, except only the bytes"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("written to DEVICE by dc3dd are verified. This"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("option can be used more than once (see note"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("#3 below for how to generate multiple outputs)."));
+      fprintf(stderr, "\t%-21s%s\n", _("fhod=DEVICE"), _("The same as phod=DEVICE, with additional"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("hashing of the entire output DEVICE. This option"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("can be used more than once (see note #3 below"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("for how to generate multiple outputs)."));
       fprintf(stderr, "\t%-21s%s\n", _("rec=off"), _("By default, zeros are written to the output(s) in"));
       fprintf(stderr, "\t%-21s%s\n", "", _("place of bad sectors when the input is a device."));
       fprintf(stderr, "\t%-21s%s\n", "", _("Use this option to cause the program to instead"));
       fprintf(stderr, "\t%-21s%s\n", "", _("exit when a bad sector is encountered.")); 
       fprintf(stderr, "\t%-21s%s\n", _("wipe=DEVICE"), _("Wipe DEVICE by writing zeros (default) or a"));
       fprintf(stderr, "\t%-21s%s\n", "", _("pattern specified by pat= or tpat=.")); 
-      fprintf(stderr, "\t%-21s%s\n", _("vwipe=DEVICE"), _("Wipe DEVICE by writing zeros (default) or a"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("pattern specified by pat= or tpat=."));
-      fprintf(stderr, "\t%-21s%s\n", "", _("Verify DEVICE after writing it by hashing it"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("and comparing the hash(es) to the input hash(es)."));
+      fprintf(stderr, "\t%-21s%s\n", _("hwipe=DEVICE"), _("Wipe DEVICE by writing zeros (default) or a"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("pattern specified by pat= or tpat=. Verify"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("DEVICE after writing it by hashing it and"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("comparing the hash(es) to the input hash(es)."));
       fprintf(stderr, "\t%-21s%s\n", _("pat=HEX"), _("Use pattern as input, writing HEX to every byte"));
       fprintf(stderr, "\t%-21s%s\n", "", _("of the output. This option can only be used once"));
       fprintf(stderr, "\t%-21s%s\n", "", _("and cannot be combined with if=, ifs=, or"));
@@ -4379,9 +4260,9 @@ usage(int status)
       fprintf(stderr, "\t%-21s%s\n", "", _("repeatedly to the output. This option can only be"));
       fprintf(stderr, "\t%-21s%s\n", "", _("used once and cannot be combined with if=, ifs=,"));
       fprintf(stderr, "\t%-21s%s\n", "", _("or pat=."));
-      fprintf(stderr, "\t%-21s%s\n", _("cnt=SECTORS"), _("Input only SECTORS input sectors. Must be used with"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("pat= or tpat= if not using the pattern"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("with wipe= or vwipe= to wipe a device."));
+      fprintf(stderr, "\t%-21s%s\n", _("cnt=SECTORS"), _("Read only SECTORS input sectors. Must be used"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("with pat= or tpat= if not using the pattern with"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("wipe= or hwipe= to wipe a device."));
       fprintf(stderr, "\t%-21s%s\n", _("iskip=SECTORS"), _("Skip SECTORS sectors at start of the input device"));
       fprintf(stderr, "\t%-21s%s\n", "", _("or file."));
       fprintf(stderr, "\t%-21s%s\n", _("oskip=SECTORS"), _("Skip SECTORS sectors at start of the output"));
@@ -4402,9 +4283,8 @@ usage(int status)
       fprintf(stderr, "\t%-21s%s\n", _("verb=on"), _("Activate verbose reporting, where sectors in/out"));
       fprintf(stderr, "\t%-21s%s\n", "", _("are reported for each file in sets of files"));
       fprintf(stderr, "\t%-21s%s\n", "", _("specified using ifs=, ofs=, or hofs=."));
-      fprintf(stderr, "\t%-21s%s\n", "", _("Alternatively, verbose reporting may be"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("activated at compile time using"));
-      fprintf(stderr, "\t%-21s%s\n", "", _("-DDEFAULT_VERBOSE_REPORTING."));
+      fprintf(stderr, "\t%-21s%s\n", "", _("Alternatively, verbose reporting may be activated"));
+      fprintf(stderr, "\t%-21s%s\n", "", _("at compile time using -DDEFAULT_VERBOSE_REPORTING."));
       fprintf(stderr, "\t%-21s%s\n", _("nwspc=on"), _("Activate compact reporting, where the use"));
       fprintf(stderr, "\t%-21s%s\n", "", _("of white space to divide log output into"));
       fprintf(stderr, "\t%-21s%s\n", "", _("logical sections is suppressed. Alternatively,"));
@@ -4430,16 +4310,16 @@ usage(int status)
       fputs(_("notes:\n"), stderr);
       fputs(_("------\n\n"), stderr);
       fputs(_("1. To read from stdin, do not specify if=, ifs=, pat=, or tpat=.\n"), stderr);
-      fputs(_("2. To write to stdout, do not specify of=, hof=, ofs=, hofs=, wipe=,\n"), stderr); 
-      fputs(_("   or vwipe=.\n"), stderr);
+      fputs(_("2. To write to stdout, do not specify of=, hof=, ofs=, hofs=, phod=,\n"), stderr); 
+      fputs(_("   fhod=, wipe=, or hwipe=.\n"), stderr);
       fputs(_("3. To write to multiple outputs specify more than one of of=, hof=, ofs=,\n"), stderr);
-      fputs(_("   or hofs=, in any combination.\n"), stderr);
+      fputs(_("   hofs=, phod=, or fhod=, in any combination.\n"), stderr);
       fputs(_("4. FMT is a pattern for a sequence of file extensions that can be numerical\n"), stderr);
       fputs(_("   starting at zero, numerical starting at one, or alphabetical. Specify FMT\n"), stderr);
       fputs(_("   by using a series of zeros, ones, or a's, respectively. The number of\n"), stderr);
       fputs(_("   characters used indicates the desired length of the extensions.\n"), stderr);
       fputs(_("   For example, a FMT specifier of 1111 indicates four character\n"), stderr);
-      fputs(_("   numerical extensions starting with 0001.\n"), stderr);  
+      fputs(_("   numerical extensions starting with 0000.\n"), stderr);  
       fputs(_("5. BYTES may be followed by the following multiplicative suffixes:\n"), stderr);
       fputs(_("   c (1), w (2), b (512), kB (1000), K (1024), MB (1000*1000),\n"), stderr);
       fputs(_("   M (1024*1024), GB (1000*1000*1000), G (1024*1024*1024), and\n"), stderr);
@@ -4460,36 +4340,43 @@ usage(int status)
 int
 main (int argc, char **argv)
 {
-   // Set up for localization.
    initialize_main(&argc, &argv);
    program_name = argv[0];
+
+   // Set up localization support.
    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
 
-   // Handle command line "help options" (i.e., --flags, --help, --version). 
-   if (argc == 2 && STREQ(argv[1], "--flags"))
-   {
+   // Handle the --flags command line option. 
+   if (argc == 2 && STREQ(argv[1], "--flags")) {
       printf("%s compiled with:\n", PROGRAM_NAME);
       report_compile_flags(stdout, true);
       exit(DC3DD_EXIT_COMPLETED);
    }
+
+   // Handle the --help and --version command line options. 
    parse_long_options(argc, argv, PROGRAM_NAME, PACKAGE, VERSION, usage, AUTHORS, (char const*)NULL);
-   if (getopt_long(argc, argv, "", NULL, NULL) != -1)
-   {
+   if (getopt_long(argc, argv, "", NULL, NULL) != -1) {
       usage(DC3DD_EXIT_FAILED);
    }
    
-   // Do the requested imaging.
    initiate_logging(argc, argv);
-   report_start_message();
-   report_settings(argc, argv);
+   
+   // Report startup info. 
+   report_startup_message();
+   report_command_line(argc, argv);
+
+   // Do the requested jobs.
    settings_t* settings = parse_settings(argc, argv);
    report_input_size(settings);
    job_t* jobs = make_jobs(settings);
    int exit_code = execute_jobs(jobs);
+
+   // Report results.
    report_results(jobs);
    report_exit_message(exit_code);
+
    terminate_logging();
 
    return exit_code;
diff --git a/tests/test-damaged-device-imaging.sh b/tests/test-damaged-device-imaging.sh
index 0f2e1d4..71f2ff5 100755
--- a/tests/test-damaged-device-imaging.sh
+++ b/tests/test-damaged-device-imaging.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-source "test-helpers.sh"
+source "helpers.sh"
 
 if [ "$1" == "" ]
 then
diff --git a/tests/test-device-imaging.sh b/tests/test-device-imaging.sh
index b47a558..6eae887 100755
--- a/tests/test-device-imaging.sh
+++ b/tests/test-device-imaging.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-source "test-helpers.sh"
+source "helpers.sh"
 
 if [ "$1" == "" ]
 then
@@ -19,8 +19,9 @@ REF_SECTORS=1952767
 REF_SECTOR_SIZE=512
 
 DC3DD="../src/dc3dd"
-TEST_IMG="/tmp/test.img"
-TEST_LOG="/tmp/test.log"
+TEST_IMG="/tmp/dev_img_test.img"
+TEST_LOG0="/tmp/dev_img_test0.log"
+TEST_LOG1="/tmp/dev_img_test1.log"
 
 init()
 {
@@ -28,12 +29,15 @@ init()
 
    echo "Getting reference image size..."
    REF_IMG_BYTES=`stat -c %s "$REF_IMG"`
+   echo "$REF_IMG_BYTES"   
 
    echo "Getting reference image md5 hash..."
    REF_IMG_MD5_HASH=`md5sum "$REF_IMG" | cut -f 1 -d \ `
+   echo "$REF_IMG_MD5_HASH"   
 
    echo "Getting reference image sha1 hash..."
    REF_IMG_SHA1_HASH=`sha1sum "$REF_IMG" | cut -f 1 -d \ `
+   echo "$REF_IMG_SHA1_HASH"   
    
    echo "Writing reference image to device..."
    umount "$DEVICE"
@@ -61,8 +65,8 @@ init()
 cleanup()
 {
     # Clean up files from any previous runs.
-    rm "$TEST_IMG" "$TEST_LOG" "$TEST_IMG".* "$TEST_LOG".* 
-    if [ -e "$TEST_IMG" -o -e "$TEST_LOG" ]
+    rm "$TEST_IMG" "$TEST_LOG0" "$TEST_LOG1" 
+    if [ -e "$TEST_IMG" -o -e "$TEST_LOG0" -o -e "$TEST_LOG1" ]
     then
         echo "Failed to remove test output files"
         exit 1
@@ -71,21 +75,24 @@ cleanup()
 
 basic()
 {
-   echo "Testing imaging of a device..." 
+   echo "Testing imaging of a device with multiple logs..." 
    cleanup
 
-   "$DC3DD" if="$DEVICE" of="$TEST_IMG" hash=md5 hash=sha1 log="$TEST_LOG"
+   "$DC3DD" if="$DEVICE" of="$TEST_IMG" hash=md5 hash=sha1 log="$TEST_LOG0" log="$TEST_LOG1"
    if [ $? -ne 0 ]
    then
-       echo "basic: basic imaging run failed"
+       echo "basic: run failed"
        exit 1
    fi
 
    echo "Checking results..."
-   check_output_file "basic:" "$TEST_IMG" "$REF_IMG_BYTES" "$REF_IMG_MD5_HASH" "$REF_IMG_SHA1_HASH"
-   check_input_with_errors_logging "basic:" "$TEST_LOG" "$REF_SECTOR_SIZE" "(probed)" "$REF_IMG_BYTES" "$REF_SECTORS" "0"
-   check_input_hash_logging "basic:" "$TEST_LOG" "$REF_IMG_MD5_HASH" "$REF_IMG_SHA1_HASH"
-   check_single_output_logging "basic:" "$TEST_LOG" "$REF_SECTORS"
+   check_output_file "basic" "$TEST_IMG" "$REF_IMG_BYTES" "$REF_IMG_MD5_HASH" "$REF_IMG_SHA1_HASH"
+   check_input_logging "basic" "$TEST_LOG0" "$REF_SECTOR_SIZE" "(probed)" "$REF_IMG_BYTES" "$REF_SECTORS" "0"
+   check_input_logging "basic" "$TEST_LOG1" "$REF_SECTOR_SIZE" "(probed)" "$REF_IMG_BYTES" "$REF_SECTORS" "0"
+   check_input_hash_logging "basic" "$TEST_LOG0" "$REF_IMG_MD5_HASH" "$REF_IMG_SHA1_HASH"
+   check_input_hash_logging "basic" "$TEST_LOG1" "$REF_IMG_MD5_HASH" "$REF_IMG_SHA1_HASH"
+   check_single_output_logging "basic" "$TEST_LOG0" "$REF_SECTORS"
+   check_single_output_logging "basic" "$TEST_LOG1" "$REF_SECTORS"
    echo "Results ok"
    echo
 }
@@ -95,10 +102,10 @@ wipe()
    echo "Testing a verified wipe of a device with zeros..."
    cleanup
 
-   "$DC3DD" vwipe="$DEVICE" hash=md5 hash=sha1 log="$TEST_LOG"
+   "$DC3DD" hwipe="$DEVICE" hash=md5 hash=sha1 log="$TEST_LOG0"
    if [ $? -ne 0 ]
    then
-       echo "wipe 00: run failed"
+       echo "wipe: run failed"
        exit 10
    fi
 
@@ -117,10 +124,10 @@ wipe()
    fi
 
    echo "Checking results..."
-   check_input_logging "wipe" "$TEST_LOG" "$REF_SECTOR_SIZE" "(probed)" "$REF_IMG_BYTES" "$REF_SECTORS"
-   check_input_hash_logging "wipe" "$TEST_LOG" "$PAT_MD5_HASH" "$PAT_SHA1_HASH"
-   check_single_output_logging "wipe" "$TEST_LOG" "$REF_SECTORS"
-   check_output_hash_logging "wipe" "$DEVICE" "$TEST_LOG"
+   check_input_logging "wipe" "$TEST_LOG0" "$REF_SECTOR_SIZE" "(probed)" "$REF_IMG_BYTES" "$REF_SECTORS"
+   check_input_hash_logging "wipe" "$TEST_LOG0" "$PAT_MD5_HASH" "$PAT_SHA1_HASH"
+   check_single_output_logging "wipe" "$TEST_LOG0" "$REF_SECTORS"
+   check_output_hash_logging "wipe" "$DEVICE" "$TEST_LOG0"
    echo "Results ok"
    echo
 }
diff --git a/tests/test-file-imaging.sh b/tests/test-file-imaging.sh
index 7d9362e..75dab44 100755
--- a/tests/test-file-imaging.sh
+++ b/tests/test-file-imaging.sh
@@ -4,13 +4,13 @@
 # Requires mktemp and stat from coreutils
 # ***************************************
 
-source "test-helpers.sh"
+source "helpers.sh"
 
 DC3DD="../src/dc3dd"
 
 REF_SECTOR_SIZE=512
 
-REF_FILE="/tmp/ref-good.img"
+REF_FILE="/tmp/ref-random-good.img"
 REF_FILE_SECTORS=452767
 REF_FILE_BYTES="0"
 REF_FILE_MD5_HASH=""
@@ -546,6 +546,8 @@ validation()
         echo "Invalid combination of if and ifs not detected properly"
         exit 13
     fi
+    echo "Results ok"
+    echo
 }
 
 init

-- 
debian-forensics/dc3dd



More information about the forensics-changes mailing list