summaryrefslogtreecommitdiffstats
path: root/src/pass.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 16:00:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 16:00:33 +0000
commit96fcf3ea3a51071dfeca141b7c9b0e5e3e5365a8 (patch)
tree51d766882e3eacda67f6a3b0df8ae3df7d157e20 /src/pass.c
parentInitial commit. (diff)
downloadnwipe-96fcf3ea3a51071dfeca141b7c9b0e5e3e5365a8.tar.xz
nwipe-96fcf3ea3a51071dfeca141b7c9b0e5e3e5365a8.zip
Adding upstream version 0.34.upstream/0.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/pass.c')
-rw-r--r--src/pass.c866
1 files changed, 866 insertions, 0 deletions
diff --git a/src/pass.c b/src/pass.c
new file mode 100644
index 0000000..3f6487a
--- /dev/null
+++ b/src/pass.c
@@ -0,0 +1,866 @@
+/*
+ * pass.c: Routines that read and write patterns to block devices.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdint.h>
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "pass.h"
+#include "logging.h"
+#include "gui.h"
+
+int nwipe_random_verify( nwipe_context_t* c )
+{
+ /**
+ * Verifies that a random pass was correctly written to the device.
+ *
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The input buffer. */
+ char* b;
+
+ /* The pattern buffer that is used to check the input buffer. */
+ char* d;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ if( c->prng_seed.s == NULL )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "Null seed pointer." );
+ return -1;
+ }
+
+ if( c->prng_seed.length <= 0 )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "The entropy length member is %i.", c->prng_seed.length );
+ return -1;
+ }
+
+ /* Create the input buffer. */
+ b = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the input buffer." );
+ return -1;
+ }
+
+ /* Create the pattern buffer */
+ d = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !d )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ free( b );
+ return -1;
+ }
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ /* FIXME: Is there a better way to handle this? */
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* Reseed the PRNG. */
+ c->prng->init( &c->prng_state, &c->prng_seed );
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ c->prng->read( &c->prng_state, d, blocksize );
+
+ /* Read the buffer in from the device. */
+ r = read( c->device_fd, b, blocksize );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to read from '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial read. */
+
+ /* The number of bytes that were not read. */
+ int s = blocksize - r;
+
+ nwipe_log(
+ NWIPE_LOG_WARNING, "%s: Partial read from '%s', %i bytes short.", __FUNCTION__, c->device_name, s );
+
+ /* Increment the error count. */
+ c->verify_errors += 1;
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial read.", c->device_name );
+ return -1;
+ }
+
+ } /* partial read */
+
+ /* Compare buffer contents. */
+ if( memcmp( b, d, blocksize ) != 0 )
+ {
+ c->verify_errors += 1;
+ }
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ pthread_testcancel();
+
+ } /* while bytes remaining */
+
+ /* Release the buffers. */
+ free( b );
+ free( d );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_random_verify */
+
+int nwipe_random_pass( NWIPE_METHOD_SIGNATURE )
+{
+ /**
+ * Writes a random pattern to the device.
+ *
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The output buffer. */
+ char* b;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ /* Number of writes to do before a fdatasync. */
+ int syncRate = nwipe_options.sync;
+
+ /* Counter to track when to do a fdatasync. */
+ int i = 0;
+
+ /* general index counter */
+ int idx;
+
+ if( c->prng_seed.s == NULL )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: Null seed pointer." );
+ return -1;
+ }
+
+ if( c->prng_seed.length <= 0 )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: The entropy length member is %i.", c->prng_seed.length );
+ return -1;
+ }
+
+ /* Create the initialised output buffer. Initialised because we don't want memory leaks
+ * to disk in the event of some future undetected bug in a prng or its implementation. */
+ b = calloc( c->device_stat.st_blksize, sizeof( char ) );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the output buffer." );
+ return -1;
+ }
+
+ /* Seed the PRNG. */
+ c->prng->init( &c->prng_state, &c->prng_seed );
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ c->prng->read( &c->prng_state, b, blocksize );
+
+ /* For the first block only, check the prng actually wrote something to the buffer */
+ if( z == c->device_size )
+ {
+ idx = c->device_stat.st_blksize;
+ while( idx > 0 )
+ {
+ if( b[idx] != 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "prng stream is active" );
+ break;
+ }
+ idx--;
+ }
+ if( idx == 0 )
+ {
+ nwipe_log( NWIPE_LOG_FATAL, "ERROR, prng wrote nothing to the buffer" );
+ return -1;
+ }
+ }
+
+ /* Write the next block out to the device. */
+ r = write( c->device_fd, b, blocksize );
+
+ /* Check the result for a fatal error. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "write" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to read from '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial write. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial write. */
+
+ /* The number of bytes that were not written. */
+ int s = blocksize - r;
+
+ /* Increment the error count by the number of bytes that were not written. */
+ c->pass_errors += s;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial write on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial write.", c->device_name );
+ return -1;
+ }
+
+ } /* partial write */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ /* Perodic Sync */
+ if( syncRate > 0 )
+ {
+ i++;
+
+ if( i >= syncRate )
+ {
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ nwipe_log( NWIPE_LOG_WARNING, "Wrote %llu bytes on '%s'.", c->pass_done, c->device_name );
+ c->fsyncdata_errors++;
+ free( b );
+ return -1;
+ }
+
+ i = 0;
+ }
+ }
+
+ pthread_testcancel();
+
+ } /* remaining bytes */
+
+ /* Release the output buffer. */
+ free( b );
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ /* FIXME: Is there a better way to handle this? */
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_random_pass */
+
+int nwipe_static_verify( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* pattern )
+{
+ /**
+ * Verifies that a static pass was correctly written to the device.
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The input buffer. */
+ char* b;
+
+ /* The pattern buffer that is used to check the input buffer. */
+ char* d;
+
+ /* A pointer into the pattern buffer. */
+ char* q;
+
+ /* The pattern buffer window offset. */
+ int w = 0;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ if( pattern == NULL )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: Null entropy pointer." );
+ return -1;
+ }
+
+ if( pattern->length <= 0 )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: The pattern length member is %i.", pattern->length );
+ return -1;
+ }
+
+ /* Create the input buffer. */
+ b = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the input buffer." );
+ return -1;
+ }
+
+ /* Create the pattern buffer */
+ d = malloc( c->device_stat.st_blksize + pattern->length * 2 );
+
+ /* Check the memory allocation. */
+ if( !d )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ free( b );
+ return -1;
+ }
+
+ for( q = d; q < d + c->device_stat.st_blksize + pattern->length; q += pattern->length )
+ {
+ /* Fill the pattern buffer with the pattern. */
+ memcpy( q, pattern->s, pattern->length );
+ }
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ /* FIXME: Is there a better way to handle this? */
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ /* Read the buffer in from the device. */
+ r = read( c->device_fd, b, blocksize );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to read from '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r == blocksize )
+ {
+ /* Check every byte in the buffer. */
+ if( memcmp( b, &d[w], r ) != 0 )
+ {
+ c->verify_errors += 1;
+ }
+ }
+ else
+ {
+ /* The number of bytes that were not read. */
+ int s = blocksize - r;
+
+ /* TODO: Handle a partial read. */
+
+ /* Increment the error count. */
+ c->verify_errors += 1;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial read on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial read.", c->device_name );
+ return -1;
+ }
+
+ } /* partial read */
+
+ /* Adjust the window. */
+ w = ( c->device_stat.st_blksize + w ) % pattern->length;
+
+ /* Intuition check:
+ * If the pattern length evenly divides the block size
+ * then ( w == 0 ) always.
+ */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ pthread_testcancel();
+
+ } /* while bytes remaining */
+
+ /* Release the buffers. */
+ free( b );
+ free( d );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_static_verify */
+
+int nwipe_static_pass( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* pattern )
+{
+ /**
+ * Writes a static pattern to the device.
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The output buffer. */
+ char* b;
+
+ /* A pointer into the output buffer. */
+ char* p;
+
+ /* The output buffer window offset. */
+ int w = 0;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ /* Number of writes to do before a fdatasync. */
+ int syncRate = nwipe_options.sync;
+
+ /* Counter to track when to do a fdatasync. */
+ int i = 0;
+
+ if( pattern == NULL )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: Null pattern pointer." );
+ return -1;
+ }
+
+ if( pattern->length <= 0 )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: The pattern length member is %i.", pattern->length );
+ return -1;
+ }
+
+ /* Create the output buffer. */
+ b = malloc( c->device_stat.st_blksize + pattern->length * 2 );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ return -1;
+ }
+
+ for( p = b; p < b + c->device_stat.st_blksize + pattern->length; p += pattern->length )
+ {
+ /* Fill the output buffer with the pattern. */
+ memcpy( p, pattern->s, pattern->length );
+ }
+ ///
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: lseek() returned a bogus offset on '%s'.", c->device_name );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ /* Write the next block out to the device. */
+ r = write( c->device_fd, &b[w], blocksize );
+
+ /* Check the result for a fatal error. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "write" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to write to '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial write. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial write. */
+
+ /* The number of bytes that were not written. */
+ int s = blocksize - r;
+
+ /* Increment the error count. */
+ c->pass_errors += s;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial write on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial write.", c->device_name );
+ return -1;
+ }
+
+ } /* partial write */
+
+ /* Adjust the window. */
+ w = ( c->device_stat.st_blksize + w ) % pattern->length;
+
+ /* Intuition check:
+ *
+ * If the pattern length evenly divides the block size
+ * then ( w == 0 ) always.
+ */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counterr. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ /* Perodic Sync */
+ if( syncRate > 0 )
+ {
+ i++;
+
+ if( i >= syncRate )
+ {
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ nwipe_log( NWIPE_LOG_WARNING, "Wrote %llu bytes on '%s'.", c->pass_done, c->device_name );
+ c->fsyncdata_errors++;
+ free( b );
+ return -1;
+ }
+
+ i = 0;
+ }
+ }
+
+ pthread_testcancel();
+
+ } /* remaining bytes */
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ /* FIXME: Is there a better way to handle this? */
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* Release the output buffer. */
+ free( b );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_static_pass */